解决bug过程中,遇到线程冲突的问题,记录一下。
微软在WPF引入了Dispatcher,那么这个Dispatcher的主要作用是什么呢?
Application.Current.Dispatcher 是 WPF(Windows Presentation Foundation)中的一个重要对象,用于处理线程相关的操作,特别是在涉及 UI 更新时。
Dispatcher 是 WPF 中实现线程同步和异步调用的核心机制之一,它基于 .NET 的 System.Windows.Threading 命名空间。每个 WPF 窗口(Window)都有一个与其关联的 Dispatcher,负责管理该窗口所在的 UI 线程的消息队列。当您在 WPF 应用程序中进行任何 UI 相关的操作(如更新控件属性、触发事件等),这些操作必须在创建该控件的 UI 线程(也称为“主线程”或“UI 线程”)上执行。这是 Windows 操作系统对 UI 线程安全性的要求。
Dispatcher的作用是用于管理线程工作项队列。
主线程负责接收输入、处理事件、绘制屏幕等工作,这样一来,UI界面是主线程创建的,因为子线程不能直接更新由主线程维护的UI界面,所有调用Dispatcher更新UI。
不管是WinForm应用程序还是WPF应用程序,实际上都是一个进程,一个进程可以包含多个线程,其中有一个是主线程,其余的是子线程。在WPF或WinForm应用程序中,主线程负责接收输入、处理事件、绘制屏幕等工作,为了使主线程及时响应,防止假死,在开发过程中对一些耗时的操作、消耗资源比较多的操作,都会去创建一个或多个子线程去完成操作,比如大数据量的循环操作、后台下载。这样一来,由于UI界面是主线程创建的,所以子线程不能直接更新由主线程维护的UI界面。
Dispatcher的作用是用于管理线程工作项队列,类似于Win32中的消息队列,Dispatcher的内部函数,仍然调用了传统的创建窗口类,创建窗口,建立消息泵等操作。Dispatcher本身是一个单例模式,构造函数私有,暴露了一个静态的CurrentDispatcher方法用于获得当前线程的Dispatcher。对于线程来说,它对Dispatcher是一无所知的,Dispatcher内部维护了一个静态的 List
方式一
- new Thread(()=>{
-
- this.Dispatcher.Invoke(new Action(()=>{
-
- //通知主线程去完成更新
-
- }));
-
- }).Start();
方式二
- Application.Current.Dispatcher.Invoke(new Action(() => {
-
- Application.Current.MainWindow.Title = "我修改过的窗体标题";
-
- }));
方式三:异步机制
- var task = Application.Current.Dispatcher.BeginInvoke(new Action(() => {
-
- Application.Current.MainWindow.Title = "我修改过的窗体标题";
-
- }));
-
- task.Completed += new EventHandler(task_Completed);
-
- static void task_Completed(object sender, EventArgs e)
-
- {
-
- MessageBox.Show("任务已经完成");
-
- }
CheckAccess 和 VerifyAccess 方法
这两个方法用于检查当前线程是否拥有对 UI 线程的访问权限。在多线程环境中,它们有助于判断是否需要通过 Invoke 或 BeginInvoke 强制切换到 UI 线程:
- if (!Application.Current.Dispatcher.CheckAccess())
- {
- Application.Current.Dispatcher.Invoke(() =>
- {
- // 在此执行任何 UI 相关的操作
- });
- }
- else
- {
- // 当前已经在 UI 线程,可以直接执行 UI 操作
- // ...
- }
使用 Application.Current.Dispatcher 主要有以下意义:
Dispatcher,您可以安全地从其他线程访问和更新 UI,避免因非法跨线程操作引发的异常。BeginInvoke),可以避免阻塞 UI 线程,保持界面响应流畅,提升用户体验。Dispatcher 提供了一种统一且易于使用的机制来处理线程间通信和同步,简化了 WPF 应用程序的多线程开发工作。在WPF中,所有的WPF对象都派生自DispatcherObject,DispatcherObject暴露了Dispatcher属性用来取得创建 对象线程对应的Dispatcher。DispatcherObject对象只能被创建它的线程所访问,其他线程修改 DispatcherObject需要取得对应的Dispatcher,调用Invoke或者BeginInvoke来投入任务。Dispatcher的一些设计思路包括 Invoke和BeginInvoke等从WinForm时代就是一直存在的,只是使用了Dispatcher来封装这些线程级的操作。
参照连接: