异步读取进程输出时的延迟

发布于 2024-10-20 05:26:35 字数 1169 浏览 3 评论 0原文

我正在使用 .NET 和 C# 启动一个进程并异步读取它的输出。我的问题是,在我的程序读取输出之前似乎存在延迟。如果我在命令行上运行可执行文件,则在它开始运行时立即有输出。但是,当我使用代码运行它时,在进程退出之前不会调用 ReadOutput 事件处理程序。我想用它来提供进程输出的实时视图,所以我不想等待(几分钟)直到进程退出。

这是一些相关代码:

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();
}

I am using .NET and C# to start a process and read it's output asynchronously. My problem is that there seems to be a delay before the output is read by my program. If I run the executable on the command line, there is output immediately when it starts running. But when I run it using my code the ReadOutput event handler isn't called until the Process exits. I want to use this to provide a real-time view of the process's output, so I don't want to wait (several minutes) until the process exits.

Here's some relevant code:

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";
}

And here's my event handler:

private void ReadOutput(object sendingProcess, DataReceivedEventArgs outLine)
{
    OutputBuilder.AppendLine(outLine.Data);
    Console.WriteLine(outLine.Data);
    Console.Out.Flush();
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

顾冷 2024-10-27 05:26:36

这是我根据我的评论使用 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;
        }

This is the way I do it (C# 3) as per my comment using the lambda syntax.

    /// <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;
        }
做个少女永远怀春 2024-10-27 05:26:36

您的方法中的问题可能是该进程仅在退出时完成一行的输出。无法控制异步事件处理程序何时触发。

在控制台应用程序中,最好的选择是定期检查新输出并同步读取和显示它:

        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 的 TimerDispatcherTimer和 WPF 分别调用循环内容并更新 UI。

请注意,我不会刷新 Console.Out,因为 Console.Write()Console.WriteLine() 会自动导致此情况。

The problem in your approach is probably that the process only finishes the output of a line when it exits. There's no way to control when the asynchronous event handlers fire.

In a console application, your best bet is to periodically check for new output and read and display it synchronously:

        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);
            }
        }

In a UI project, you'd use Timer or DispatcherTimer for WinForms and WPF, respectively, to call the contents of the loop and update the UI.

Note that I don't flush Console.Out, as Console.Write() and Console.WriteLine() cause this automatically.

忆梦 2024-10-27 05:26:36

尝试添加 MyProcess.WaitForExit 方法调用:

MyProcess.BeginOutputReadLine();
MyProcess.BeginErrorReadLine();

// will wait for the associated process to exit
MyProcess.WaitForExit();

Try to add MyProcess.WaitForExit method call:

MyProcess.BeginOutputReadLine();
MyProcess.BeginErrorReadLine();

// will wait for the associated process to exit
MyProcess.WaitForExit();
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文