我应该使用Invoke还是SynchronizationContext来从另一个线程更新窗体控件?

6

我试图理解如何从其他线程更新UI控件。

目前使用BeginInvoke,它工作得很好,但我一直听说你也可以使用SynchronizationContext来完成相同的事情。

哪种方法更好?

此外,从线程更新UI是否是不良做法?是否最好引发事件并由主窗体处理,或者还有其他更可取的方法?

对于这个有点主观的问题,我感到抱歉,但在线程世界中有很多选择,我正在努力理解它们之间的差异以及每个选择的适用范围,以及撰写可读性和可扩展性代码的最佳实践。

编辑:现在我也看到了TaskScheduler.FromCurrentSynchronizationContext的路线...太多选择了x_x


你应该通常倾向于使用更高级别的抽象来进行线程处理,以帮助自己成功地避免陷阱。正确地处理线程是困难的。建议使用Task或BackgroundWorker。 - Hans Passant
2个回答

7
我更喜欢使用 SynchronizationContext 而不是 Control.InvokeControl.Invoke 的危险在于它所属的Control存在生命期问题。 如果在尝试调用时关闭了 Control,则会损害调用成功的能力。 这会在关闭对话框、视图转移等情况下发生。 SynchronizationContext.Current 通常与其关联的线程一样长寿。 它确实有一个有限的生命周期,因此最终会出现相同的问题,但比 Control 更可预测。

3
我认为这个句子不正确:“如果在您尝试调用控件时,该控件被处理掉了,那么它会损害调用成功的能力...”,因为实际上 WindowsFormsSynchronizationContext 是 SynchronizationContext 的具体实现,使用的正是 Control.Invoke 进行编排:Control control = this.controlToSendTo; object[] objArray = new object[] { state }; control.Invoke(d, objArray);。我更喜欢 SynchronizationContext,因为它是一个抽象概念,如果 Microsoft 更改了具体实现,您就不必更改代码。 - Michael Denny
@MichaelDenny,controlToSendToSyncContext维护,并具有与线程相同的生命周期,因此不会被处理,而Control.Invoke的控件可能已经被处理,例如当窗体关闭时。 - Eben

0

你有考虑过使用后台工作组件吗?对于长时间运行的任务,它是一种干净且简单的方法来获得多线程能力。例如,您可以使用ProgressChanged事件和BackgroundWorker来执行UI的更新,而BackgroundWorker类将确保创建BW的线程是执行ProcessChanged和WorkComplete事件的线程。因此,如果您从UI创建了BW并设置它开始工作,那么您可以从那里安全地更新UI。

这里是来自MS的快速文章 http://msdn.microsoft.com/en-us/library/cc221403%28v=vs.95%29.aspx

另一个非常好的链接 http://www.albahari.com/threading/part3.aspx#_BackgroundWorker


我熟悉BackgroundWorker,但是这个项目是为服务器应用程序设计的,因此我认为Tasks / ThreadPool更合适。否则,您是正确的。我可能应该在原始帖子中包含它。对此我道歉。 - John Smith
@John 为什么你的服务器应用程序上有用户界面控件? - adrianm
在第二次阅读问题后,我认为John在这里提出了两个问题。一是使用同步,而是最好的处理UI中的多线程的方法。 - Jason Turan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接