Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Synchronization of asynchronous reading from stdout of child process

I have application that launches sub process and processes its stdout asynchronously. The problem is that the async operation takes some time and I want method responsible for process execution to end after all async IO operations are done.

I have code like this:

using System.Diagnostics;

Process process = new Process();
// ...
process.OutputDataReceived += new DataReceivedEventHandler(this.OnRecvStdOut);
process.ErrorDataReceived += new DataReceivedEventHandler(this.OnRecvStdErr);
// ...
process.Start();
process.BeginOutputReadLine();
process.BeginErrorReadLine();
// ...
process.WaitForExit();

Now I'm looking for a way how to tell the program to wait until all IO (OnRecvStdOut) operations are done.

I though about using one of System.Threading classes, but I'm not sure which class is the best for this and how to do this after all, the best way would probably be:

public void OnRecvStdOut(...)
{
    something.Increase();
    // the stuff that takes so long
    something.DecreaseAndSignal();
}

And in main function:

something.WaitUntilZero();

Note: I'd like to allow both StdErr and StdOut to be processed in parallel. Something can't rely that Wait will be called as much as Signal, because Increase() and DecreaseAndSignal() pairs will be called many times before Wait occurs.

The second thing that came to mind was something that would be able to signal many times (without requirement to process signals) and use loop in main function like:

while( ioOperations > 0){
    something.WaitForSignal(500);
}

Edit: Current working solution:

I came up with this what seems to be working:

using System.Threading;

protected int IOCount = 0;
protected AutoResetEvent _IOSyncEvent = new AutoResetEvent(false);

public void OnRecvStdOut(...)
{
    Interlocked.Increase( ref IOCount);
    // the stuff that takes so long
    Interlocked.Decrease( ref IOCount);
    IOSyncEvent.Set();
}

// After process.WaitForExit()
while( IOCount > 0){
    // 250 for a case that signal occurs after condition and before wait
    IOSyncEvent.WaitOne(250); 
}

But I'd appreciate any notes how to do this "good practice way" or what are possible risks of implementation I though of.

like image 818
Vyktor Avatar asked Oct 29 '12 11:10

Vyktor


1 Answers

Use a CountdownEvent set to 2. Each data received callback, check the x.EndOfStream flag (where x is standardoutput or standarderror on the process object). If EOF, then signal the countdownevent. It will work because both the standard and error output streams will eventually signal the event. Once both signal it, the wait on it will return. It is also a very efficient way to do it.

like image 70
doug65536 Avatar answered Oct 11 '22 13:10

doug65536