2012-07-24 48 views
2

我发现对于我目前的情况看起来像一个非常简单的解决方案。使用匿名方法从单独的线程更新GUI的说明

我目前的情况是,我想对新线程做一些I/O操作,这样我就不会陷入我的GUI线程。我有一个函数,作为我的表单的一员,已经完成了这些I/O操作,但是在GUI线程上运行它确实使应用程序很难使用。所以我的计划是在一个新的线程中运行这个函数。所以,我在表单中创建了一个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 

从该响应的外观上来看,我可以在一个新的线程中运行该功能,然后可以使用匿名函数来更新自己的状态,当线程完成计算。尽管如此,我还不足以填补答复的空白。

所有我似乎读到的线程说,我的ThreadStart函数需要是一个新类中的静态方法。这个回应似乎表明我可以在我的Form类中完成它,所以this引用仍然引用我的Form实例。否则,如果我的ThreadStart参数是一个不同的类,我不得不传入对Form实例的引用,这看起来像更多的代码,对吧?

有人会帮我填写该回复的上下文吗?提前致谢!

回答

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表达式,或者你甚至可以删除lambda表达式,并把方法有直接,只要你确保签名是正确的。如果需要,您也可以继续链接这些延续,例如,允许您执行任务1,更新标签,然后执行任务2,更新标签等。主要缺点是它不擅长更新进度条经常在一个循环内部,方式可以是BackgroundWorker

+0

哦,真棒。我不知道这存在!非常感谢你。 – Ryan 2012-07-24 16:35:29