当控制台输出被重定向到文本框时发生跨线程操作
本文关键字:操作 线程 文本 输出 控制台 重定向 | 更新日期: 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是否从不同的线程调用,而不是从拥有表单的线程调用。如果是,它将要求拥有表单的线程再次调用该方法,只在正确的线程上。