WPF启动后的两个线程:一个线程负责呈现,它隐藏在后台运行。另一个线程负责UI界面管理,主要接收收入、处理事件、绘制屏幕以及运行应用程序代码,即UI线程。
WPF中每个界面及界面中的控件都继承自DispatcherObject类,DispatcherObject包含一个公共属性Dispatcher。当前执行的主窗体及各个控件,在初始化时就都自动赋值了Dispatcher属性,并且都指向同一个UI线程所拥有的Dispatcher对象。
1.Dispatcher两个注册工作项的方法:
Invoke 和 BeginInvoke。这两个方法都是通过调度一个委托来执行。其中Invoke 是同步调用,也就是说,直到 UI 线程实际执行完该委托它才返回。而BeginInvoke是异步的,将立即返回。
2.Dispatcher的使用:
1).解决“从一个非 UI 线程中更新一个由UI线程创建的对象”。
在一个子线程中直接访问或更新UI线程创建的对象,会报异常。因为DispatcherObject对象只能被创建它的线程所访问,其他线程修改 DispatcherObject需要取得对应的Dispatcher,调用Invoke或者BeginInvoke来投入任务。所以此时我们可以利用Dispatcher的Invoke或BeginInvoke方法来完成对UI线程上控件的更新。
例如:另一个线程中更新Lable控件的内容
//新建线程
Thread thread = new Thread(UpdateLableText);
thread.Start();
private void UpdateLableText()
{
this.Dispatcher.BeginInvoke(DispatcherPriority.Normal,
(ThreadStart) delegate()
{
lable1.Text = "Hello";
}
);
}
2).避免在Dispatcher中处理耗时操作
Dispatcher会给UI的工作队列进行优先级排队。当某一个耗时操作发生在UI线程上时,Dispatcher工作队列中其他项就需要等待,可能会出现用户界面卡死的现象。所以建议开一个新线程去处理那些耗时的操作,处理完后,可以通过向UI线程的Dispatcher队列注册工作项来通知UI线程更新结果即可。也可以说,Dispatcher利用主线程进行任务优先级的排列来模拟多线程,因此其中实质是单线程。
比如一个求和显示的耗时操作,建议新开一个线程来完成耗时的求和运算,得出求和结果后,在调用主线程Dispatcher来显示结果。
Thread thread = new Thread(UpdateLableText);
thread.Start();
private void UpdateLableText()
{
string result = GetSum(); //将耗时的操作放在thread线程中处理,而不是Dispatcher中
this.Dispatcher.BeginInvoke(DispatcherPriority.Normal,
(ThreadStart) delegate()
{
//string result = GetSum(); //一个耗时的求和操作
lable1.Text = result;
}
);
}