Reputation: 63728
I have a fairly standard setup:
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);
}
}
It all works great, I thought, until I did my first trial run and after a couple of minutes perfect running it crashed:
Unhandled Exception: Unhandled Exception: System.IndexOutOfRangeException: Proba ble I/O race condition detected while copying memory. The I/O package is not thr ead safe by default. In multithreaded applications, a stream must be accessed in a thread-safe way, such as a thread-safe wrapper returned by TextReader's or Te xtWriter's Synchronized methods. This also applies to classes like StreamWriter and StreamReader. at System.Buffer.InternalBlockCopy(Array src, Int32 srcOffset, Array dst, Int 32 dstOffset, Int32 count) at System.IO.StreamWriter.Write(Char[] buffer, Int32 index, Int32 count) at System.IO.TextWriter.WriteLine(String value)
I'm only running a single thread in my app so the only way I can think this happened is if stdout & stderror both fire events at the same time.
What should my code look like to implement the "thread-safe wrappers" mentioned?
Upvotes: 1
Views: 1012
Reputation: 33381
This should solve your problem:
private readonly object _looker = new object();
private void OutputHandler(object sendingProcess, DataReceivedEventArgs outLine)
{
if (!String.IsNullOrEmpty(outLine.Data))
{
lock(_locker)
{
sw.WriteLine(outLine.Data);
}
}
}
Another solution can be adding outputs to, for instance, ConcurrentQueue
, then retrieve messages in separate thread and save to stream.
Upvotes: 1
Reputation: 31616
Where ever you have a WriteLine put a lock around it such as
public class MyClass
{
object myLockObject = new object();
public void MyOperationCalledOnAnEvent(string data)
{
lock (myLockObject)
sw.WriteLine(outLine.Data);
}
}
Upvotes: 1