这是我用来启动子进程并监视其输出的C#代码:
using (process = new Process()) {
process.StartInfo.FileName = executable;
process.StartInfo.Arguments = args;
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardInput = true;
process.StartInfo.CreateNoWindow = true;
process.Start();
using (StreamReader sr = process.StandardOutput) {
string line = null;
while ((line = sr.ReadLine()) != null) {
processOutput(line);
}
}
if (process.ExitCode == 0) {
jobStatus.State = ActionState.CompletedNormally;
jobStatus.Progress = 100;
} else {
jobStatus.State = ActionState.CompletedAbnormally;
}
OnStatusUpdated(jobStatus);
}
我在单独的ThreadPool线程中启动多个子进程(但在四核机器上一次不超过四个)。一切正常。
我遇到的问题是我的一个子进程将退出,但对sr.ReadLine()
的相应调用将阻塞,直到我的一个子进程退出。我不确定它会返回什么,但除非有我遗漏的东西,否则不应该发生这种情况。
我的子进程没有任何关系会导致它们以任何方式“链接” - 它们不会相互通信。我甚至可以在发生这种情况时查看任务管理器/进程资源管理器,并看到我的子进程实际已退出,但是对其标准输出的ReadLine()
的调用仍然阻塞!
我已经能够通过将输出监控代码转换为新线程并执行process.WaitForExit()
来解决这个问题,但这似乎是非常奇怪的行为。有谁知道这里发生了什么?
答案 0 :(得分:2)
关于ProcessStartInfo.RedirectStandardOutput的MSDN文档详细讨论了在执行此操作时可能出现的死锁。提供了一个使用ReadToEnd
的解决方案,但我想在您使用ReadLine
时会采用相同的建议和补救措施。
引入同步读取操作 调用者之间的依赖关系 从StandardOutput流中读取 和孩子的过程写作 流。这些依赖关系可能会导致 死锁条件。当来电者 从重定向的流中读取 子进程,它依赖于 儿童。呼叫者等待阅读 操作直到孩子写信给 流或关闭流。什么时候 子进程写入足够的数据 为了填补其重定向流,它是 依赖于父母。孩子 进程等待下一次写入 操作直到父读 完整流或关闭流。 死锁条件导致 调用者和子进程等待 彼此完成一项操作, 而且都不能继续。您可以 通过评估来避免死锁 调用者和调用者之间的依赖关系 儿童过程。
最佳解决方案似乎是异步I / O而不是同步方法:
您可以使用异步读取 避免这些依赖关系的操作 和他们的僵局潜力。 或者,你可以避免 通过创建两个来实现死锁条件 线程和读取每个的输出 在一个单独的线程上流。
如果您走这条路线,有一个样本here应该对您有用。
答案 1 :(得分:0)
我认为这不是你的代码问题。阻止呼叫可以出于多种原因解锁,这不仅仅是因为他们的任务已经完成。
我不知道Windows,我必须承认,但在Unix世界中,当一个孩子完成时,会向父进程发送一个信号,这会使他从任何阻塞调用中唤醒。这将解除对父母所期望的任何输入的读取。
如果Windows工作方式相似,我不会感到惊讶。在任何情况下,请阅读阻止呼叫可能解锁的原因。