【问题标题】:Asynchronously writing a spawned process' stdout+stderr to a file in a thread-safe way in .NET在 .NET 中以线程安全的方式将生成的进程的 stdout+stderr 异步写入文件
【发布时间】:2013-01-11 21:12:29
【问题描述】:

我有一个相当标准的设置:

    void Run()
    {
        this.sw = File.CreateText(logfile)

        start.RedirectStandardInput = true;
        start.RedirectStandardOutput = true;
        start.RedirectStandardError = true;
        start.UseShellExecute = false;
        Process proc = Process.Start(start)
        proc.OutputDataReceived += new DataReceivedEventHandler(OutputHandler);
        proc.ErrorDataReceived += new DataReceivedEventHandler(OutputHandler);
        proc.BeginOutputReadLine();
        proc.BeginErrorReadLine();
        ...
    }

    private void OutputHandler(object sendingProcess, DataReceivedEventArgs outLine)
    {
        if (!String.IsNullOrEmpty(outLine.Data))
        {
            sw.WriteLine(outLine.Data);
        }
    }

我想,这一切都很好,直到我第一次试运行,几分钟后完美运行它崩溃了:

未处理的异常:未处理的异常: System.IndexOutOfRangeException:检测到可能的 I/O 竞争条件 在复制内存时。默认情况下,I/O 包不是线程安全的。 在多线程应用程序中,流必须在一个 线程安全的方式,例如由返回的线程安全包装器 TextReader 或 TextWriter 的同步方法。这也适用 到 StreamWriter 和 StreamReader 等类。在 System.Buffer.InternalBlockCopy(Array src, Int32 srcOffset, Array dst, Int 32 dstOffset,Int32 计数)在 System.IO.StreamWriter.Write(Char[] 缓冲区,Int32 索引,Int32 计数) 在 System.IO.TextWriter.WriteLine(字符串值)

我只在我的应用程序中运行一个线程,所以我认为发生这种情况的唯一方法是如果 stdout 和 stderror 同时触发事件。

实现上述“线程安全包装器”的代码应该是什么样的?

【问题讨论】:

  • 您可能应该编辑您的问题并制作一个简短但完整的程序,以便不太高级的用户在遇到问题时也能理解。
  • 哇,很棒的异常消息。

标签: c# .net .net-2.0


【解决方案1】:

如果你有一个 WriteLine 在它周围加个锁,比如

public class MyClass
{
   object myLockObject = new object();

   public void MyOperationCalledOnAnEvent(string data)
   {
      lock (myLockObject)
          sw.WriteLine(outLine.Data);
   } 

}

【讨论】:

    【解决方案2】:

    这应该可以解决您的问题:

    private readonly object _looker = new object();
    
    private void OutputHandler(object sendingProcess, DataReceivedEventArgs outLine)
        {
            if (!String.IsNullOrEmpty(outLine.Data))
            {
                lock(_locker)
                {
                    sw.WriteLine(outLine.Data);
                }
            }
        }
    

    另一种解决方案是将输出添加到例如ConcurrentQueue,然后在单独的线程中检索消息并保存到流中。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-08-19
      • 2013-02-26
      • 2021-11-06
      • 1970-01-01
      相关资源
      最近更新 更多