应用程序调度器和控制调度器

3
假设我有一个名为“button1”的控制按钮和一个名为“doSomething”的函数,该函数从另一个线程调用。
我有两种方法可以从UI线程调用函数doSomething。
首先,从控制按钮分派程序。
this.button1.Dispatcher.BeginInvoke(new Action(() => { doSomething(); }));

并且,第二个问题是来自应用程序调度器。
this.Dispatcher.BeginInvoke(new Action(() => { doSomething(); }));

结果是相同的,那么真正的区别在哪里呢?
3个回答

6

同一线程拥有的所有控件引用相同的调度程序实例。这没有区别。


你的意思是Dispatcher是静态的并且由一个线程(UI线程)拥有吗? - Hensembryan
@brian:它并不是静态的,就像静态字段是静态的那样,每个线程只有一个实例。应用程序引用UI线程的调度程序。 - H.B.
1
那个函数是从它们的父类(Window或Controls)继承而来,并由UI线程拥有,我说得对吗? - Hensembryan
1
@brian:它不是继承而来的,而是来自基类型DispatcherObject的构造函数,MSDN所说:“在构造时,DispatcherObject存储对与当前运行线程链接的Dispatcher的引用。” - H.B.

3
所有的UI控件(正常创建的)都共享同一个调度程序实例。该调度程序在UI线程上工作。如果您在后台线程上创建一些控件,它将在该线程上创建新的调度程序,这并不是很好。 在WinForms和WPF中避免大多数线程和UI控件问题的最佳方法是使用System.Threading.SynchronizationContext.Current。工作流程很简单:当您在UI线程上时,获取System.Threading.SynchronizationContext.Current并将其保存在某个位置(例如公共静态字段)中。然后,每当您想要在UI线程上运行一些代码时,访问该持久化的SynchronizationContext实例并使用其Send或Post方法。它将在SynchronizationContext实现的线程上运行您的委托(对于当前情况,在UI线程上)。它还足够智能,可以使用当前的调用方式(对于WinForms的消息循环和WPF的调度程序),并且如果您正在从UI线程调用,则会同步运行您的委托。 只需记住,在当前UI线程上创建第一个控件后,应该获取SynchronizationContext,因为SynchronizationContext将在那之后初始化。

单例调度程序怎么样?我认为调度程序比同步上下文简单得多。 - Hensembryan
当然,你可以将Dispatcher保存为单例。只需在使用上几乎与SynchronizationContext相同,这样你的代码将适用于WinForms、Wpf和可能的未来框架。 - Vladimir Perevalov
仅使用Application.Current.Dispatcher.Invoke(...)在UI线程上调用方法不是可以吗? - Mariusz Schimke
有没有办法访问渲染线程?我的控件有我想要的文本,但旧值仍然显示在屏幕上,我想等到它被绘制出来再进行操作。 - Paul McCarthy

0
在大多数情况下,我们只有一个UI线程。因此,无论您如何调用,都不会有任何区别。
control.Dispatcher(which comes inherited from DispatcherObject parent of the controls).

or 

Disptacher.Current.

但是有些情况下,您可能会遇到多个调度程序的情况。 因此,在这种情况下,Control.Dispatcher将有所帮助,因为它将找出当前的调度程序以尊重线程亲和性。在这种情况下,Dispatcher.Current将无法帮助。

一个场景是,为显示繁忙指示器而专门设置了一个线程(带有调度程序),因为默认UI线程正在渲染大量控件列表。

然而,使用SynchronizationContext是避免此类问题的另一种方法。但是,如果该上下文或线程不再需要或已被其他开发人员设置为null,那么在我看来,始终选择Control.Dispatcher是明智的。


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