当控制台输出被重定向到文本框时发生跨线程操作

本文关键字:操作 线程 文本 输出 控制台 重定向 | 更新日期: 2023-09-27 18:01:57

我是c#和GUI开发的新手。如果这是一个老问题,请告诉我来源,我会把这个问题写下来。

我的情况:所以我正在编写一个GUI,将一个函数的控制台输出重定向到我的Windows应用程序表单中的文本框。Console输出仅向用户显示设备序列号、当前软件版本等信息。

我的问题:这个表单工作得很好,除了更新软件的过程需要几分钟冻结我的表单,直到它完成。现在,我知道实现一个后台工作器将会缓解这个问题。然而,当我实现后台工作器时,我收到以下错误。

跨线程操作无效:控制"文本框"从创建它的线程以外的线程访问。

我的代码总结如下:

public class Form1 : Form
{
    this.backgroundWorker1.DoWork += new
    System.ComponentModel.DoWorkEventHandler(this.backgroundWorker1_DoWork);
    private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
    {
         BackgroundWorker bw = sender as BackgroundWorker;     
         BigProcess(bw);
    }
    private void BigProcess(BackgroundWorker bw)
    {
        // lengthy operation that includes lots of
        Console.WriteLine("feedback stuff for the user");
    }
    private void button1_Click(object sender, EventArgs e)
    {
        this.backgroundWorker1.RunWorkerAsync();
    }
}

在这个项目中,我还有一个从StringWriter派生的类TextBoxStreamWriter,它为我处理控制台输出的重定向。在TextBoxStreamWriter中,我重写了WriteLine方法和Write方法。

下面是我正在做的一个例子:

public override void WriteLine(string value)
{
      base.WriteLine(DateTime.Now.ToString(value));
      textBoxOutput.AppendText(value.ToString() + Environment.NewLine);
      writer.Write(value);
}

调用此方法时抛出InvalidOperationException。

如何使这个线程安全?任何帮助都会很感激。

当控制台输出被重定向到文本框时发生跨线程操作

你只需要将文本框的更新返回到UI线程中-例如:

if (textBoxOutput.InvokeRequired)
{
  Invoke((MethodInvoker)(() => textBoxOutput.AppendText(value.ToString() + Environment.NewLine);
}
else
{
   textBoxOutput.AppendText(value.ToString() + Environment.NewLine)
}

BackgroundWorker公开了一个事件:RunWorkerCompleted,该事件用于使用长时间运行操作的结果更新UI。该事件将在UI线程中触发。

所以你所需要做的就是处理这个事件,而不是手动编组到UI线程。

backgroundWorker1.RunWorkerCompleted += (s, args)=> 
    textBoxOutput.AppendText(args.Result.ToString() + Environment.NewLine);

然后你可以把DoWork中的结果设置为你想要打印的任何内容。

如果你想在整个后台处理过程中更新UI,那么你可以有效地使用"进度更新"。BGW内置了对在整个后台工作过程中更新UI的支持。参考MSDN页面BGW的例子

试试这个:

public override void WriteLine(string value)
{
    if (InvokeRequired)
    {
        Invoke(() => WriteLine(value));
        return;
    }
    base.WriteLine(DateTime.Now.ToString(value));
    textBoxOutput.AppendText(value.ToString() + Environment.NewLine);
    writer.Write(value);
}

这将检查WriteLine是否从不同的线程调用,而不是从拥有表单的线程调用。如果是,它将要求拥有表单的线程再次调用该方法,只在正确的线程上。