澄清使用匿名方法从单独的线程更新GUI

时间:2012-07-24 16:28:00

标签: c# winforms multithreading user-interface

我发现对我目前的情况来说,这似乎是一个非常简单的解决方案。

我目前的情况是我想在一个新的线程上做一些I / O操作,所以我不会陷入我的GUI线程。我有一个函数,作为我的Form的成员,已经执行了这些I / O操作,但在GUI Thread上运行它确实使应用程序很难使用。所以我的计划是在新的Thread中运行这个函数。所以,我以我的形式创建了一个Thread变量,并试图让它将该函数用作ThreadStart参数。但它似乎并不喜欢它。

我找到了一个优雅的解决方案,作为对另一个帖子here的回复。

///...blah blah updating files
string newText = "abc"; // running on worker thread
this.Invoke((MethodInvoker)delegate {
    someLabel.Text = newText; // runs on UI thread
});
///...blah blah more updating files

从该响应的外观来看,我可以在新的Thread中运行此函数,然后在Thread完成计算时使用匿名函数来更新我的Form。不过,我只是不足以填补那个回复中的空白。

我似乎读到的关于Threads的所有内容都说我的ThreadStart函数需要是新类中的静态方法。该响应似乎表明我可以在我的Form类中执行它,因此this引用仍然引用我的Form实例。否则,如果我的ThreadStart参数是一个不同的类,我必须传入对Form实例的引用,这似乎更多的代码,对吧?

有人会介意帮助我填写回复的背景吗?提前谢谢!

1 个答案:

答案 0 :(得分:1)

有很多方法可以做到这一点。一个非常简单,直截了当的版本就是使用BackgroundWorker。它专为这种情况而设计。它有一个在后台线程中运行的DoWork方法,以及在UI线程中运行的工作完成后触发的Completed事件(因此您不需要调用invoke或任何东西)用结果更新UI)。它甚至内置了报告进度支持(报告进度事件也在UI线程中运行),因此您可以轻松更新进度条或状态文本。

MSDN也有一些examples,你可以通过一些简单的搜索找到更多。

通过C#4.0提供的另一个选项是使用Tasks。您可以启动一个将在后台线程中运行的新任务,然后您可以添加一个将在UI线程中的延续。

这是一个简单的例子:

private void button1_Click(object sender, EventArgs e)
{
    Task.Factory.StartNew(() => doStuffInBackground())
        .ContinueWith(task => updateUI(), TaskScheduler.FromCurrentSynchronizationContext());
}

private void updateUI()
{
    throw new NotImplementedException();
}

private void doStuffInBackground()
{
    throw new NotImplementedException();
}

你当然可以在我所拥有的实际lambda中做任何你想做的事,或者你甚至可以删除lambdas并直接将方法放在那里,只要你确保签名是正确的。如果需要,您还可以继续链接这些延续,例如,允许您执行任务1,更新标签,然后执行任务2,更新标签等。主要缺点是它不擅长更新进度条经常在循环内部,BackgroundWorker的方式。