1

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

    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 的 Synchronized 方法返回的线程安全包装器。这也适用于 StreamWriter 和 StreamReader 等类。在 System.IO.StreamWriter.Write(Char[] buffer, Int32 index, Int32 count) 在 System.IO.TextWriter 的 System.Buffer.InternalBlockCopy(Array src, Int32 srcOffset, Array dst, Int 32 dstOffset, Int32 count)。写行(字符串值)

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

我的代码应该是什么样子来实现提到的“线程安全包装器”?

4

2 回答 2

1

无论你在哪里有一个 WriteLine 都会在它周围加个锁,例如

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

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

}
于 2013-01-11T12:49:42.367 回答
1

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

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然后在单独的线程中检索消息并保存到流中。

于 2013-01-11T12:49:50.347 回答