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



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

启动每个进程后,我将其添加到List<ActiveProcessJob>中,并通过构造一个new ManualResetEvent( false )并将其SafeWaitHandle属性分配给new SafeWaitHandle( my_process.Handle, false ),将进程的WaitHandle存储在ActiveProcessJobCompleteEvent属性中。

我通过以下代码等待进程完成(以及任何"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 );

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

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

我几乎需要知道进程何时"即将退出",因为否则我不知道何时会出现最后一个OutputDataReceived事件,该事件显然在与我的WaitAny调用不同的线程上运行,等待进程终止。

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

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

Process.Exit 不能保证在所有输出事件完成之前运行,这可能是由于疏忽,即它在调用 RaiseOnExit 之前不会等待流的结束,除非您调用 Process.WaitForExit,这是我通过使用 .NET Reflector 反编译代码确定的。

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

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

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

最新更新