线程可随时终止时,如何可靠获取外部进程的输出与错误信息?
可靠获取外部进程全部输出/错误信息的解决方案
你的问题核心在于:进程退出后,异步读取回调(OutputDataReceived/ErrorDataReceived)可能还没处理完最后一批数据,而你的循环一检测到HasExited就直接返回,导致末尾内容丢失。Thread.Sleep是治标不治本的办法,我们可以用同步事件来精准等待所有异步读取完成,同时兼顾线程随时被终止的限制。
解决方案思路
- 用
ManualResetEvent标记输出/错误流的读取完成状态:当回调收到null(表示流已关闭,所有数据已发送)时,触发对应的事件。 - 在循环中,同时监听线程终止信号和这两个事件的完成信号,既保证能拿到全部数据,又能及时响应线程终止请求。
完整代码实现
// 定义两个事件,标记输出和错误流读取完成 var outputDone = new ManualResetEvent(false); var errorDone = new ManualResetEvent(false); prc = new Process(); prc.StartInfo.UseShellExecute = false; prc.StartInfo.FileName = fileName; prc.StartInfo.Arguments = arguments; prc.StartInfo.LoadUserProfile = true; prc.StartInfo.RedirectStandardOutput = true; prc.StartInfo.RedirectStandardError = true; // 输出回调:收到null时标记输出完成 prc.OutputDataReceived += (sendingProcess, outLine) => { if (outLine.Data != null) { outputText.AppendLine(outLine.Data); } else { outputDone.Set(); } }; // 错误回调:收到null时标记错误完成 prc.ErrorDataReceived += (sendingProcess, errorLine) => { if (errorLine.Data != null) { errorText.AppendLine(errorLine.Data); } else { errorDone.Set(); } }; prc.Start(); prc.BeginOutputReadLine(); prc.BeginErrorReadLine(); // 循环等待:要么线程被终止,要么两个流都读取完成 while (!Thread.CurrentThread.ThreadState.HasFlag(ThreadState.AbortRequested) && !Thread.CurrentThread.ThreadState.HasFlag(ThreadState.Aborted)) { // 等待两个事件完成,超时100ms以便检查线程状态 var waitResult = WaitHandle.WaitAll(new[] { outputDone, errorDone }, 100); if (waitResult) { // 所有数据已读取完毕,返回退出码 return prc.ExitCode; } // 未超时则继续循环,检查线程状态 } // 如果线程被终止,这里可以做清理(比如关闭进程) prc?.Kill(); return -1; // 或者返回自定义的终止码
关键细节说明
ManualResetEvent的作用:当进程的标准输出/错误流关闭时,.NET会触发一次回调并传入null,这是所有数据读取完成的信号,此时设置事件表示该流已处理完毕。WaitHandle.WaitAll带超时:避免无限阻塞,每100ms检查一次线程是否被终止,既保证响应速度,又不会浪费CPU资源。- 线程终止时的清理:如果线程被终止,记得主动杀死外部进程,避免它成为孤儿进程。
这个方案既不需要依赖Thread.Sleep的玄学延迟,又能可靠获取全部输出/错误信息,同时完全兼容线程随时被终止的限制。
内容的提问来源于stack exchange,提问作者user2363676
相关产品推荐
相关产品推荐

