StandardOutput.EndOfStream挂起

我正在我的C#应用​​程序中启动一个运行控制台应用程序的进程。 我重定向了标准输入和输出,并且能够通过StandardOutput.ReadLine()读取几行。 我确信我已正确配置ProcessStartInfo。

控制台应用程序在启动时输出几行(以“标记”行结束),然后等待输入。 收到输入后,它再次输出几行(再次以“标记”行结束),依此类推。 我的目的是从它读取行直到我收到“marker”行,此时我知道发送相应的输入字符串。

我的问题是,经过几次迭代,程序挂起。 暂停调试器往往会将挂起置于对StandardOutput.EndOfStream的调用中。 以下测试代码就是这种情况:

while (!mProcess.StandardOutput.EndOfStream) // Program hangs here. { Console.WriteLine(mProcess.StandardOutput.ReadLine()); } 

当我测试“marker”行时,如果我在读取行后尝试访问StandardOutput.EndOfStream,我会得到同样的挂起:

 string line = ""; while (!isMarker(line)) { line = mProcess.StandardOutput.ReadLine(); } bool eos = mProcess.StandardOutput.EndOfStream; // Program hangs here. 

我可能会做什么导致这个属性表现如此可怕?

您无法在此处可靠地使用EndOfStream。 如果没有任何缓冲的字符,StreamReader.EndOfStream属性将调用StandardOutput.Read()。 如果进程没有向其输出管道发送任何内容并且不关闭它,那么Read()调用将阻塞。 由于它将等待输入,因此几乎可以保证发生这种情况。 在进程关闭输出管道的末尾并且StreamReader消耗了所有缓冲的字符之前,EndOfStream不会返回true。 程序终止时。

使用BeginOutputReadLine()可能是检测“标记”行的更好方法。 请注意,回调发生在另一个线程上。 另请注意,没有必要等待进程发送标记,您编写的任何内容都将被缓冲,直到进程准备好读取它为止。 请注意缓冲区很小,可能会出现死锁。

有许多路径可以在与流程类交互时创建死锁。 Microsoft在MSDN网站上对其进行了描述。 这是我如何称呼它。 请注意ErrorDataReceived和OutputDataReceived的处理以及对BeginErrorReadLine和BeginOutputReadLine的调用。 这通过让父进程异步读取流来消除死锁方案。 注意:RunProcessResponse是我自己的小包装器数据传输对象。

 Public Function RunProcess(ByVal executableFileName As String, ByVal arguments As String, ByVal workingDirectory As System.String) As RunProcessResponse Dim process As System.Diagnostics.Process = Nothing Dim response As RunProcessResponse Try process = New System.Diagnostics.Process() Dim psInfo As New System.Diagnostics.ProcessStartInfo() Dim errorString As System.String = String.Empty Dim outputString As System.String = String.Empty If Not System.String.IsNullOrEmpty(workingDirectory) Then psInfo.WorkingDirectory = workingDirectory End If psInfo.FileName = executableFileName psInfo.Arguments = arguments psInfo.WindowStyle = ProcessWindowStyle.Hidden psInfo.CreateNoWindow = True psInfo.RedirectStandardError = True psInfo.RedirectStandardOutput = True psInfo.UseShellExecute = False AddHandler process.ErrorDataReceived, Sub(sender As Object, args As DataReceivedEventArgs) If args.Data IsNot Nothing Then errorString &= args.Data & vbCrLf End If End Sub AddHandler process.OutputDataReceived, Sub(sender As Object, args As DataReceivedEventArgs) If args.Data IsNot Nothing Then outputString &= args.Data & vbCrLf End If End Sub process.StartInfo = psInfo process.Start() process.BeginErrorReadLine() process.BeginOutputReadLine() process.WaitForExit() response = New RunProcessResponse(errorString, outputString, process.ExitCode) Return response Finally If process IsNot Nothing Then process.Dispose() End If End Try End Function 

在阅读标准输出之前,您是否等待该过程完成:

 mProcess.WaitForExit(); 

如果你知道在你的后面没有更多的标准输入:

 while (!mProcess.StandardOutput.EndOfStream) 

循环,您可以关闭标准输入:

 mProcess.StandardInput.Close(); 

表示没有更多输入。 只要标准输入打开,就有可能获得更多输入,从而产生更多输出; 因此,标准输出永远不会达到EndOfStream。