文章目录
- 一、Dispatcher
- 1.1 定义
- 1.2 示例
- 1.3 附注
- 二、DispatcherObject
- 2.1 定义
- 2.2 附注
- 2.3 线程安全性
- ⭐2.4 哪些东西是DispatcherObject
- 三、Dispatcher与DispatcherObject
- 3.1 WPF中的应用场景
一、Dispatcher
译为调度程序/调度器。
1.1 定义
提供用于管理线程工作项队列的服务。
所以它是一个抽象的东西,用于管理线程工作项的。
1.2 示例
下面示例演示了如何在Dispatcher中放置一个操作。
首先,创建一个无参的delegate(委托)。
public delegate void NextPrimeDelegate();
接下来,调用BeginInvoke(DispatcherPriority, Delegate)。该调用传入两个参数:
- DispatcherPriority,调用优先级,设置为DispatcherPriority.Normal
- 回调函数,通过委托NextPrimeDelegate的一个实例传入
startStopButton.Dispatcher.BeginInvoke(
DispatcherPriority.Normal,
new NextPrimeDelegate(CheckNextNumber));
1.3 附注
Dispatcher为特定线程维护一个优先级队列,队列中里面含有工作项,
在线程上创建了一个Dispatcher后,即使Dispatcher已关闭,该线程也只能与该Dispatcher关联。
若你尝试为当前线程获取CurrentDispatcher,但当前线程没有关联的Dispatcher,则会创建一个Dispatcher。 当你创建一个DispatcherObject时,也会创建一个Dispatcher。 若你在后台线程创建了一个Dispatcher,请确保在退出线程之前关闭该Dispatcher。
若一个Dispatcher被关闭了,则不能被重启。
在WPF中,只有与之关联的Dispatcher才能访问DispatcherObject(这是为了保护程序,增加程序安全性;否则新手乱跨线程访问UI资源,程序动不动就会崩溃)。例如,后台线程无法更新与UI线程上的Dispatcher关联的Button的内容。 为了使后台线程可以访问Button的Content属性,必须将作业委托给UI线程关联的Dispatcher。 可以使用Invoke或BeginInvoke来完成此操作。Invoke是同步的,而BeginInvoke是异步的。操作会添加到指定DispatcherPriority的Dispatcher队列中。
如果在一个已经关闭的Dispatcher上调用BeginInvoke,返回DispatcherOperation的status属性被设置为Aborted。
除了DisableProcessing,Dispatcher上的所有方法都是自由线程的(free-threaded)。
从DispatcherObject派生的对象具有线程亲缘性/亲和性(thread affinity)。
从Freezable派生的对象在冻结时是自由线程的。
自由线程
就是多个线程可以自由访问。不过这不意味着多线程访问是安全的,可能需要自己加锁来实现访问安全性。
线程亲缘性
指某些资源或对象与特定线程的关联性。这意味着这些资源或对象只能由与之关联的线程进行访问和操作。在多线程编程中,线程亲和性是一种保护机制,可以防止来自其他线程的未经授权的访问和冲突。
二、DispatcherObject
2.1 定义
表示一个与Dispatcher关联的对象。
2.2 附注
只有创建Dispatcher的线程才能直接访问DispatcherObject。要从其他线程访问DispatcherObject,得在与DispatcherObject关联的Dispatcher上调用Invoke或BeginInvoke。
需要强制线程安全的DispatcherObject子类可以在所有公有方法上调用VerifyAccess。这样可以确保调用线程是创建DispatcherObject的线程。
DispatcherObject不能单独实例化;也就是说,所有构造函数都是protected。
从DispatcherObject派生的对象具有线程亲缘性。
从Freezable派生的对象在冻结时是自由线程的。
2.3 线程安全性
该类的所有公有的静态成员都是线程安全的。任何实例成员都不保证线程安全。
此对象只能从创建它的线程访问。尝试从其他线程访问时将抛出InvalidOperationException。Invoke(DispatcherPriority, Delegate) 或BeginInvoke(DispatcherPriority, Delegate) 为将工作引导至正确的线程提供支持。
⭐2.4 哪些东西是DispatcherObject
讲了这么久的DispatcherObject,那到底实际编程中哪些东西是DispatcherObject?
我截了个Button类的继承关系图:
可以看到,Object派生了DispatcherObject,而DispatcherObject与我们平时接触的控件(Control)之间还隔了那么多层关系。这表示DispatcherObject的层次相当高,很多类都派生自它。
具体地,如各种常见控件、窗体等。
三、Dispatcher与DispatcherObject
根据上面两节描述,我画了一张Dispatcher与DispatcherObject的关系图。
学过计算机的都知道,线程是调度单元,它是用来执行代码的。
dispatcher作为调度程序,会把队列中排好的作业项(从第一节来看,应该是委托?)喂给线程执行。
该线程中创建的dispatcherObject将与dispatcher产生关联。
3.1 WPF中的应用场景
第一节中提到,在WPF中,只有与线程关联的Dispatcher才能访问DispatcherObject。
新手可能会这样使用:后台开个线程,去修改UI窗口中控件的内容,于是运行遇到不支持跨线程访问的报错。解决方案也很简单,用this.Dispatcher.Invoke/BeginInvoke即可;但是呢,这本质上还是单线程的。建议把耗时的数据处理放到后台线程中执行,待数据处理完毕,再在UI线程中对数据进行显示。