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

时间:2013-04-19 00:27:30

标签: .net stdout race-condition io-redirection

以非常受控制的方式,我将作业排在ProcessJobManager中,一次处理最多X个并发进程。

在开始每个流程后,我将其添加到List<ActiveProcessJob>,并通过构建{{1}在WaitHandle的{​​{1}}媒体资源中为该流程存储ActiveProcessJob并将其CompleteEvent属性分配给new ManualResetEvent( false )

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

SafeWaitHandle

这可以正确检测一个或多个进程(或添加到队列中的项目)的终止;但是,我还重定向标准输出流并调用new SafeWaitHandle( my_process.Handle, false )以确保触发WaitHandle[] wait_handles = active_jobs .Select<ActiveProcessJob,WaitHandle>( j => j.CompleteEvent ) .Union( new WaitHandle[]{ new_jobs_queued} ).ToArray(); WaitHandle.WaitAny( wait_handles ); 个事件。

问题是经常会检测并处理进程终止(在Process.BeginOutputReadLine事件的事件处理程序最后一次触发之前,它会从Process.OutputDataReceived列表中删除。在这种情况下, handler不能引用Process,因为它已经从队列中删除了。

我几乎需要知道某个进程何时“即将退出”,否则我不知道何时会发生上一个active_jobs事件,该事件显然是在我Process.OutputDataReceived的单独线程上运行的等待进程终止的调用。

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

1 个答案:

答案 0 :(得分:5)

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

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

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

  

“标准输出重定向到异步事件时   处理程序,输出处理可能没有   此方法返回时完成。确保异步事件   处理完成后,调用WaitForExit()重载   从此过载中接收到true后不接受任何参数。“

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