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

时间:2013-03-01 14:14:27

标签: c# multithreading winforms

我是C#和GUI开发的新手。如果这是一个老问题,请直接告诉我该来源,我将删除这个问题。

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

我的问题:此表单效果很好,但更新软件的过程需要几分钟时间才会冻结我的表单,直到完成为止。现在,我知道实现后台工作程序可以缓解这个问题。但是,当我实现后台工作程序时,我收到以下错误。

  

跨线程操作无效:控制' TextBox'从创建它的线程以外的线程访问。

我的代码摘要如下:

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

在这个项目中,我还有一个从TextBoxStreamWriter派生的类StringWriter,它正在为我重定向控制台输出。在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。

如何使这个线程安全?任何帮助,将不胜感激。

3 个答案:

答案 0 :(得分:1)

您只需要将文本框的更新恢复到UI线程 - 例如:

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

答案 1 :(得分:1)

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

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

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

然后,您可以将DoWork中的结果设置为您想要打印的内容。

如果要在整个后台处理过程中更新UI,则可以有效地进行“进度更新”。 BGW内置支持在整个后台工作中更新UI。有关示例,请参阅BGW的MSDN页面。

答案 2 :(得分:0)

试试这个:

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。如果是,它将要求拥有该表单的线程再次调用该方法,仅在正确的线程上。

相关问题