C#:读取子进程stdout块直到另一个子进程完成?

时间:2010-10-29 20:50:46

标签: c# pipe subprocess

这是我用来启动子进程并监视其输出的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()来解决这个问题,但这似乎是非常奇怪的行为。有谁知道这里发生了什么?

2 个答案:

答案 0 :(得分:2)

关于ProcessStartInfo.RedirectStandardOutput的MSDN文档详细讨论了在执行此操作时可能出现的死锁。提供了一个使用ReadToEnd的解决方案,但我想在您使用ReadLine时会采用相同的建议和补救措施。

  

引入同步读取操作   调用者之间的依赖关系   从StandardOutput流中读取   和孩子的过程写作   流。这些依赖关系可能会导致   死锁条件。当来电者   从重定向的流中读取   子进程,它依赖于   儿童。呼叫者等待阅读   操作直到孩子写信给   流或关闭流。什么时候   子进程写入足够的数据   为了填补其重定向流,它是   依赖于父母。孩子   进程等待下一次写入   操作直到父读   完整流或关闭流。   死锁条件导致   调用者和子进程等待   彼此完成一项操作,   而且都不能继续。您可以   通过评估来避免死锁   调用者和调用者之间的依赖关系   儿童过程。

最佳解决方案似乎是异步I / O而不是同步方法:

  

您可以使用异步读取   避免这些依赖关系的操作   和他们的僵局潜力。   或者,你可以避免   通过创建两个来实现死锁条件   线程和读取每个的输出   在一个单独的线程上流。

如果您走这条路线,有一个样本here应该对您有用。

答案 1 :(得分:0)

我认为这不是你的代码问题。阻止呼叫可以出于多种原因解锁,这不仅仅是因为他们的任务已经完成。

我不知道Windows,我必须承认,但在Unix世界中,当一个孩子完成时,会向父进程发送一个信号,这会使他从任何阻塞调用中唤醒。这将解除对父母所期望的任何输入的读取。

如果Windows工作方式相似,我不会感到惊讶。在任何情况下,请阅读阻止呼叫可能解锁的原因。