Skip to main content
 首页 » 编程设计

c#之如何在没有死锁的情况下正确读取 c# 进程标准错误流

2024年02月25日36haluo1

根据 MSDN,我真的感觉自己在遵循最佳实践.但我很可能遗漏了一些东西,因为我的代码在从这一行继续后挂起:string errorOutput = cmd.StandardError.ReadToEnd();。我做错了什么?

        var batchfile = File.OpenWrite("run.bat"); 
        StreamWriter writer = new StreamWriter(batchfile); 
        writer.Write("dotnet run" + '\n'); 
        //writer.Write("set /p temp=\"Hit enter to continue\"" + '\n'); 
        writer.Close(); 
 
        var cmd = new Process(); 
        cmd.StartInfo.FileName = batchfile.Name; 
        cmd.StartInfo.UseShellExecute = false; 
        cmd.StartInfo.RedirectStandardError = true; 
 
        cmd.Start(); 
        string errorOutput = cmd.StandardError.ReadToEnd(); 
        cmd.WaitForExit(); 
 
        var outputfile = File.OpenWrite("run_errorOut.txt"); 
        StreamWriter outputWriter = new StreamWriter(outputfile); 
        outputWriter.Write(errorOutput); 
        outputWriter.Close(); 

如果有帮助:目前这段代码正在 dotnetcore2.2 应用程序(其目标框架是“netcoreapp2.2”)的 xunit 测试中运行。

请您参考如下方法:

所以不久前我遇到了一个非常相似的问题,几乎完全一样,我发现的是:

  • 尝试在没有任何可读 block 的情况下读取 StandardError/Output block ,直到有可读的东西为止(如果没有任何内容可读则死锁 - 例如进程已退出)
  • cmd.HasExited 在读取 StandardError/Output 之前为 false,因此无法用于检测何时完成(然后读取 StandardError/Output)

我发现最可靠的方法是在 Process 上使用提供的事件:

var outData = new StringBuilder(); 
var errData = new StringBuilder();     
 
cmd.OutputDataReceived += (sender, args) => outData.Append(args.Data ?? string.Empty); 
cmd.ErrorDataReceived += (sender, args) => errData.Append(args.Data ?? string.Empty); 

然后在 cmd.Start() == true 之后:

cmd.BeginErrorReadLine(); 
cmd.BeginOutputReadLine(); 

开始触发事件。

进程退出后,您可以在 StringBuilder 上调用 .ToString() 来获取数据:

Console.WriteLine(outData.ToString()); 
Console.WriteLine(errData.ToString()); 

(注意 .ToString() 在使用 Console.WriteLine 时可以是隐式的)