C# 异步读取进程输出时的延迟
我使用.NET和C#启动进程并异步读取其输出。我的问题是,在我的程序读取输出之前,似乎有一个延迟。如果我在命令行上运行可执行文件,当它开始运行时会立即有输出。但是,当我使用代码运行它时,在进程退出之前,不会调用ReadOutput事件处理程序。我想用它来提供进程输出的实时视图,所以我不想等待(几分钟)直到进程退出 以下是一些相关代码:C# 异步读取进程输出时的延迟,c#,.net,asynchronous,process,C#,.net,Asynchronous,Process,我使用.NET和C#启动进程并异步读取其输出。我的问题是,在我的程序读取输出之前,似乎有一个延迟。如果我在命令行上运行可执行文件,当它开始运行时会立即有输出。但是,当我使用代码运行它时,在进程退出之前,不会调用ReadOutput事件处理程序。我想用它来提供进程输出的实时视图,所以我不想等待(几分钟)直到进程退出 以下是一些相关代码: MyProcess = new Process(); MyProcess.StartInfo.FileName = command; MyProcess.Star
MyProcess = new Process();
MyProcess.StartInfo.FileName = command;
MyProcess.StartInfo.Arguments = args;
MyProcess.StartInfo.UseShellExecute = false;
MyProcess.StartInfo.RedirectStandardOutput = true;
MyProcess.StartInfo.RedirectStandardError = true;
MyProcess.StartInfo.RedirectStandardInput = true;
MyProcess.OutputDataReceived += new DataReceivedEventHandler(ReadOutput);
MyProcess.ErrorDataReceived += new DataReceivedEventHandler(ReadOutput);
if (!MyProcess.Start())
{
throw new Exception("Process could not be started");
}
try
{
MyProcess.BeginOutputReadLine();
MyProcess.BeginErrorReadLine();
}
catch (Exception ex)
{
throw new Exception("Unable to begin asynchronous reading from process";
}
下面是我的事件处理程序:
private void ReadOutput(object sendingProcess, DataReceivedEventArgs outLine)
{
OutputBuilder.AppendLine(outLine.Data);
Console.WriteLine(outLine.Data);
Console.Out.Flush();
}
根据我使用lambda语法的评论,这是我做这件事的方式(C#3)
/// <summary>
/// Collects standard output text from the launched program.
/// </summary>
private static readonly StringBuilder outputText = new StringBuilder();
/// <summary>
/// Collects standard error text from the launched program.
/// </summary>
private static readonly StringBuilder errorText = new StringBuilder();
/// <summary>
/// The program's entry point.
/// </summary>
/// <param name="args">The command-line arguments.</param>
/// <returns>The exit code.</returns>
private static int Main(string[] args)
{
using (var process = Process.Start(new ProcessStartInfo(
"program.exe",
args)
{
CreateNoWindow = true,
ErrorDialog = false,
RedirectStandardError = true,
RedirectStandardOutput = true,
UseShellExecute = false
}))
{
process.OutputDataReceived += (sendingProcess, outLine) =>
outputText.AppendLine(outLine.Data);
process.ErrorDataReceived += (sendingProcess, errorLine) =>
errorText.AppendLine(errorLine.Data);
process.BeginOutputReadLine();
process.BeginErrorReadLine();
process.WaitForExit();
Console.WriteLine(errorText.ToString());
Console.WriteLine(outputText.ToString());
return process.ExitCode;
}
//
///从启动的程序中收集标准输出文本。
///
私有静态只读StringBuilder outputText=new StringBuilder();
///
///从启动的程序中收集标准错误文本。
///
私有静态只读StringBuilder errorText=new StringBuilder();
///
///程序的入口点。
///
///命令行参数。
///退出代码。
私有静态int Main(字符串[]args)
{
使用(var process=process.Start)(新的ProcessStartInfo(
“program.exe”,
args)
{
CreateNoWindow=true,
ErrorDialog=false,
RedirectStandardError=true,
重定向标准输出=真,
UseShellExecute=false
}))
{
process.OutputDataReceived+=(发送流程、大纲)=>
outputText.AppendLine(outLine.Data);
process.ErrorDataReceived+=(sendingProcess,errorLine)=>
errorText.AppendLine(errorLine.Data);
process.BeginOutputReadLine();
process.BeginErrorReadLine();
process.WaitForExit();
Console.WriteLine(errorText.ToString());
Console.WriteLine(outputText.ToString());
返回进程.ExitCode;
}
尝试添加方法调用:
MyProcess.BeginOutputReadLine();
MyProcess.BeginErrorReadLine();
// will wait for the associated process to exit
MyProcess.WaitForExit();
您的方法中的问题可能是流程只有在退出时才完成一行的输出。无法控制异步事件处理程序何时启动 在控制台应用程序中,最好定期检查新输出,并同步读取和显示:
while (!p.HasExited)
{
if (!p.StandardOutput.EndOfStream)
{
errorBuilder.Append(p.StandardError.ReadToEnd());
outputBuilder.Append(p.StandardOutput.ReadToEnd());
Console.Write(p.StandardOutput);
}
else
{
Thread.Sleep(200);
}
}
在UI项目中,您将分别对WinForms和WPF使用定时器
或分派器
,以调用循环的内容并更新UI
请注意,我不会刷新
Console.Out
,因为Console.Write()
和Console.WriteLine()
会自动导致这种情况。您使用相同的方法来处理OutputData和ErrorData的异步事件。我不会这样设计它。特别是因为OutputBuilder
成员(我想是一个StringBuilder
)正在从两个线程进行不安全的访问。我会将其分为两个方法,并将每个方法记录到一个单独的StringBuilder
实例。这里的答案似乎都不能解决问题。我遇到了一个完全相同的问题,即进程早就结束了,但流仍在以某种方式写入。更新:我添加了一个循环延迟。我在某个地方读到200毫秒是有意识地注册一个事件所需的平均时间,因此这应该是低CPU使用率和流畅输出之间的一个很好的折衷。似乎StandardOutput.EndOfStream
有时会挂起,直到流关闭。我尝试了@Tamschi和我的程序中建议的更改ram开始挂起。当我使用调试器连接到进程时,它显示它挂起在EndOfStream
属性上。另一篇帖子()指示此属性可能会挂起,但我不明白原因。是否有人知道此属性挂起的原因或有其他想法?MSDN文档解释了上述StandardOutput代码可能导致死锁,进程将无限期等待StandardOutput流关闭。