调用StandardOutput.ReadToEndAsync时代码最后一行挂起是什么原因?
挂起问题成因
- 核心原因是你启动的
cmd.exe执行完curl命令后不会主动退出,会持续等待新的输入指令,StandardOutput.ReadToEndAsync()需要等待进程输出流完全关闭才会返回,进程不退出、输出流不结束,自然就会永久挂起。 - 另一个潜在死锁诱因:你只读取了标准输出流,没有处理标准错误流,如果进程往标准错误写了较多内容,缓冲区被占满后操作系统会强制挂起进程,等待缓冲区被读取,也会导致程序无响应。
基于异步的优雅解决方案
如果不需要用cmd执行多条命令,推荐直接启动目标进程,不要套一层cmd,实现更简洁:
var process = new Process { StartInfo = new ProcessStartInfo { FileName = "curl", Arguments = "http://example.com", RedirectStandardOutput = true, RedirectStandardError = true, UseShellExecute = false, CreateNoWindow = true } }; process.Start(); // 同时异步读取两个输出流,避免缓冲区满导致死锁 var outputTask = process.StandardOutput.ReadToEndAsync(); var errorTask = process.StandardError.ReadToEndAsync(); // 等待进程退出和两个读取任务全部完成 await Task.WhenAll(process.WaitForExitAsync(), outputTask, errorTask); // 拿到最终结果 var response = outputTask.Result; var errorInfo = errorTask.Result;
如果确实需要依赖cmd执行多条命令,主动关闭标准输入通知cmd没有后续指令即可:
var process = new Process { StartInfo = new ProcessStartInfo { FileName = "cmd.exe", RedirectStandardOutput = true, RedirectStandardInput = true, RedirectStandardError = true, UseShellExecute = false, CreateNoWindow = true } }; process.Start(); // 写入需要执行的所有命令 await process.StandardInput.WriteLineAsync("curl http://example.com"); // 关键:关闭标准输入,cmd收到信号后执行完命令就会自动退出 process.StandardInput.Close(); var outputTask = process.StandardOutput.ReadToEndAsync(); var errorTask = process.StandardError.ReadToEndAsync(); await Task.WhenAll(process.WaitForExitAsync(), outputTask, errorTask); var response = outputTask.Result;
注:
WaitForExitAsync是.NET 5及以上版本自带的原生异步API,如果使用低于.NET 5的版本,只需对进程退出事件做简单的异步封装即可,不需要使用老旧的Begin/End模式。
内容的提问来源于stack exchange,提问作者JohnWick
相关产品推荐
相关产品推荐

