【问题标题】:How do I redirect a console program's output to a text box in a thread safe way?如何以线程安全的方式将控制台程序的输出重定向到文本框?
【发布时间】:2011-05-26 02:37:16
【问题描述】:

我无法将控制台输出重定向到 Windows 窗体文本框。问题与线程有关。我正在通过以下方式运行控制台应用程序,

private void RunConsoleApp()
{
    Process proc = new Process();
    proc.StartInfo.FileName = "app.exe";
    proc.StartInfo.Arguments = "-a -b -c";
    proc.StartInfo.UseShellExecute = false;

    // set up output redirection
    proc.StartInfo.RedirectStandardOutput = true;
    proc.StartInfo.RedirectStandardError = true;    
    proc.EnableRaisingEvents = true;
    proc.StartInfo.CreateNoWindow = true;

    // Set the data received handlers
    proc.ErrorDataReceived += proc_DataReceived;
    proc.OutputDataReceived += proc_DataReceived;

    proc.Start();
    proc.BeginErrorReadLine();
    proc.BeginOutputReadLine();
    proc.WaitForExit();

    if (proc.ExitCode == 0)
    {
        out_txtbx.AppendText("Success." + Environment.NewLine);
    }
    else
    {
        out_txtbx.AppendText("Failed." + Environment.NewLine);
    }
}

然后使用此输出处理程序捕获和处理数据,

// Handle the date received by the console process
void proc_DataReceived(object sender, DataReceivedEventArgs e)
{
    if (e.Data != null)
    {
        if ((e.Data.EndsWith("DONE.")) || (e.Data.EndsWith("FAILED.")) ||
            (e.Data.StartsWith("RESET")))
        {
            // This crashes the application, but is supposedly the correct method
            this.AppendText(e.Data + Environment.NewLine);

            // This works, but the debugger keeps warning me that the call
            // is not thread safe
            //out_txtbx.AppendText(e.Data + Environment.NewLine);
        }
    }
}

然后像这样附加控制台文本,

delegate void AppendTextDelegate(string text);

// Thread-safe method of appending text to the console box
private void AppendText(string text)
{
    // Use a delegate if called from a different thread,
    // else just append the text directly
    if (this.out_txtbx.InvokeRequired)
    {
        // Application crashes when this line is executed
        out_txtbx.Invoke(new AppendTextDelegate(this.AppendText), new object[] { text });
    }
    else
    {
        this.out_txtbx.AppendText(text);
    }
}

从我看到的所有文档和示例中,这似乎是正确的方法,只是在调用 out_txtbx.Invoke 时它会使应用程序崩溃。

什么可能被破坏,有什么替代方法可以做到这一点?


解决方案(正如 Hans Passant 所指出的)

问题是应用程序由于这条线而陷入了“致命的拥抱”,

proc.WaitForExit();

该行应该被删除,方法应该是这样的,

private void RunConsoleApp()
{
    Process proc = new Process();
    proc.StartInfo.FileName = "app.exe";
    proc.StartInfo.Arguments = "-a -b -c";
    proc.StartInfo.UseShellExecute = false;

    // set up output redirection
    proc.StartInfo.RedirectStandardOutput = true;
    proc.StartInfo.RedirectStandardError = true;    
    proc.EnableRaisingEvents = true;
    proc.StartInfo.CreateNoWindow = true;

    // Set the data received handlers
    proc.ErrorDataReceived += proc_DataReceived;
    proc.OutputDataReceived += proc_DataReceived;

    // Configure the process exited event
    proc.Exited += new EventHandler(ProcExited);

    proc.Start();
    proc.BeginErrorReadLine();
    proc.BeginOutputReadLine();

    // This blocks the main thread and results in "deadly embrace"
    // The Process.Exited event should be used to avoid this.
    //proc.WaitForExit();
}

并且应该提供一个事件处理程序,

/// <summary>
/// Actions to take when console process completes
/// </summary>
private void ProcExited(object sender, System.EventArgs e)
{
    Process proc = (Process)sender;

    // Wait a short while to allow all console output to be processed and appended
    // before appending the success/fail message.
    Thread.Sleep(40);

    if (proc.ExitCode == 0)
    {
        this.AppendText("Success." + Environment.NewLine);
        ExitBootloader();
    }
    else
    {
        this.AppendText("Failed." + Environment.NewLine);
    }

    proc.Close();
}

【问题讨论】:

  • 我这样做没有错误。如果到我可以找到我的代码示例时没有足够的答案,我会发布。请注意,当我回到家时,这将是今晚晚些时候。我创建了一个写入文本框的 TextWriter 对象,然后将 Console.SetOut 定向到 TextWriter。见msdn.microsoft.com/en-us/library/…
  • @decyclone:他说在调用 out_txtbx.Invoke 的那一行。
  • @SLaks 没有错误,当我运行它或尝试进入/越过 out_txtbx.Invoke 行时,应用程序就会挂起。 Visual C# 在调试时似乎没有提供暂停(仅在断点处暂停),所以我不知道在该调用之后会发生什么。
  • Winforms 主线程是否忙于做其他事情?
  • 点击Debug工具栏上的Pause按钮,或者在VS中按Ctrl+Pause。

标签: c# multithreading winforms textbox console-application


【解决方案1】:
proc.WaitForExit();

这叫做死锁。你的主线程被阻塞,等待进程退出。这使它无法履行基本职责。就像保持 UI 更新一样。并确保调度 Control.Invoke() 请求。这会阻止 AppendText() 方法完成。这会停止退出过程。这会阻止您的 UI 线程通过 WaitForExit() 调用。 “致命的拥抱”,又名僵局。

你不能阻塞你的主线程。请改用 Process.Exited 事件。

【讨论】:

    【解决方案2】:

    试试

    out_txtbx.Invoke(new AppendTextDelegate(this.AppendText), text);
    

    【讨论】:

      猜你喜欢
      • 2010-09-29
      • 1970-01-01
      • 2011-02-22
      • 2010-10-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-08-08
      相关资源
      最近更新 更多