5

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

启动每个进程后,我将它添加到 aList<ActiveProcessJob>并将进程的 a 存储WaitHandle在我ActiveProcessJobCompleteEvent属性中,方法是构造 anew ManualResetEvent( false )并将其SafeWaitHandle属性分配给new 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 );

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

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

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

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

4

1 回答 1

5

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

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

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

“当标准输出被重定向到异步事件处理程序时,有可能在此方法返回时输出处理尚未完成。为确保异步事件处理已完成,请在接收到a后调用不带参数的WaitForExit()重载从这个过载来看是真的。”

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

于 2013-04-19T01:09:35.263 回答