如何避免进程终止通知和标准输出重定向事件之间的竞争条件?

Tri*_*nko 5 .net stdout race-condition io-redirection

以非常受控制的方式,我ProcessJobManager将作业排队,一次处理多达X个并发进程.

在开始每个进程之后,我将它添加到a List<ActiveProcessJob>并通过构造a 并将其属性分配给WaitHandleActiveProcessJobCompleteEvent属性来存储进程.new ManualResetEvent( false )SafeWaitHandlenew SafeWaitHandle( my_process.Handle, false )

我通过以下代码等待进程完成(以及任何'new_jobs_queued'):

WaitHandle[] wait_handles =
    active_jobs
    .Select<ActiveProcessJob,WaitHandle>( j => j.CompleteEvent )
    .Union( new WaitHandle[]{ new_jobs_queued} ).ToArray();
WaitHandle.WaitAny( wait_handles );
Run Code Online (Sandbox Code Playgroud)

这可以正确检测一个或多个进程(或添加到队列中的项目)的终止; 但是,我还重定向标准输出流并调用Process.BeginOutputReadLine以确保Process.OutputDataReceived触发事件.

问题是经常检测和处理进程终止(active_jobs在事件的事件处理程序Process.OutputDataReceived最后一次触发之前将其从列表中删除.在这种情况下,处理程序无法引用进程,因为它已被删除从队列中.

我几乎需要知道一个进程何时"即将退出",因为否则我不知道何时期望最后一个OutputDataReceived事件,它显然在我的WaitAny调用的一个单独的线程上运行,等待进程终止.

也许保证在最后一个Process.OutputDataReceived事件之后和Process.HasExited方法返回true之前调用Process.Exit?我需要这样的确定性.

Tri*_*nko 5

由于重定向的输出流在与处理进程终止信号的线程不同的线程中运行,并且与其无法以其他方式同步,因此处理终止信号的线程可能会在处理线程之前执行其所有工作.标准输出事件运行或完成.

Process.Exit不能保证在所有输出事件完成之前运行,可能是由于在调用RaiseOnExited之前不等待流结束的疏忽,除非你调用Process.WaitForExit,我已经确定使用.NET Reflector反编译代码.

更新:我实际上在文档中发现了这一点,这似乎表明您必须调用WaitForExit TWICE以确保它已完成:

"当标准输出被重定向到异步事件处理程序时,有可能在此方法返回时输出处理不会完成.为确保异步事件处理已完成,请在收到之后调用WaitForExit()重载,该重载不带参数从这种过载中得知."

原始我认为解决方法可能是在继续从作业队列中删除对进程的引用之前调用Process.CancelOutputRead来删除关联的事件侦听器,但这可能不起作用,因为Process.CancelOutputRead实际上没有进行任何同步清理; 它只是设置了一面旗帜.除了接受输出事件处理程序可能在进程终止处理程序完成并处理错误之后运行之外,似乎唯一真正的解决方法是在收到进程终止信号后调用WaitForExit().