• WPF使用Dispatcher


    解决bug过程中,遇到线程冲突的问题,记录一下。

    Dispatcher介绍

    微软在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 _dispatchers, 每当使用CurrentDispatcher方法时,它会在这个_dispatchers中遍历,如果没有找到,则创建一个新的Dispatcher对 象,加入到_dispatchers中去。Dispatcher内部维护了一个Thread的属性,创建Dispatcher时会把当前线程赋值给这个 Thread的属性,下次遍历查找的时候就使用这个字段来匹配是否在_dispatchers中已经保存了当前线程的Dispatcher。


    实例:WPF更新UI

    方式一

    1. new Thread(()=>{
    2.     this.Dispatcher.Invoke(new Action(()=>{
    3.         //通知主线程去完成更新
    4.     }));
    5. }).Start();

     方式二

    1. Application.Current.Dispatcher.Invoke(new Action(() => {
    2.     Application.Current.MainWindow.Title = "我修改过的窗体标题";
    3. }));

     方式三:异步机制

    1. var task = Application.Current.Dispatcher.BeginInvoke(new Action(() => {
    2.     Application.Current.MainWindow.Title = "我修改过的窗体标题";
    3. }));
    4. task.Completed += new EventHandler(task_Completed);
    5. static void task_Completed(object sender, EventArgs e)
    6. {
    7.     MessageBox.Show("任务已经完成");
    8. }

    CheckAccess 和 VerifyAccess 方法

    这两个方法用于检查当前线程是否拥有对 UI 线程的访问权限。在多线程环境中,它们有助于判断是否需要通过 Invoke 或 BeginInvoke 强制切换到 UI 线程:

    1. if (!Application.Current.Dispatcher.CheckAccess())
    2. {
    3. Application.Current.Dispatcher.Invoke(() =>
    4. {
    5. // 在此执行任何 UI 相关的操作
    6. });
    7. }
    8. else
    9. {
    10. // 当前已经在 UI 线程,可以直接执行 UI 操作
    11. // ...
    12. }

    小结

    使用 Application.Current.Dispatcher 主要有以下意义:

    • 确保 UI 线程安全:WPF 控件只能由创建它们的 UI 线程修改。通过 Dispatcher,您可以安全地从其他线程访问和更新 UI,避免因非法跨线程操作引发的异常。
    • 优化应用性能:通过异步调用(如 BeginInvoke),可以避免阻塞 UI 线程,保持界面响应流畅,提升用户体验。
    • 简化多线程编程Dispatcher 提供了一种统一且易于使用的机制来处理线程间通信和同步,简化了 WPF 应用程序的多线程开发工作。

    在WPF中,所有的WPF对象都派生自DispatcherObject,DispatcherObject暴露了Dispatcher属性用来取得创建 对象线程对应的Dispatcher。DispatcherObject对象只能被创建它的线程所访问,其他线程修改 DispatcherObject需要取得对应的Dispatcher,调用Invoke或者BeginInvoke来投入任务。Dispatcher的一些设计思路包括 Invoke和BeginInvoke等从WinForm时代就是一直存在的,只是使用了Dispatcher来封装这些线程级的操作。

    参照连接:

    WPF Dispatcher介绍 - microsoftzhcn - 博客园 (cnblogs.com)

    WPF入门教程系列四——Dispatcher介绍 - DotNet菜园 - 博客园 (cnblogs.com)

  • 相关阅读:
    字节跳动面试官:请你实现一个大文件上传和断点续传
    Xilinx ZYNQ 7000学习笔记五(Xilinx SDK 烧写镜像文件)
    html5 checkbox
    Keil实现Flash升级跳转(STM32/GD32/HC32)
    《统计学习方法》——第六章 逻辑斯谛回归与最大熵模型
    numpy生成0和1数组方法,从已有数组生成新数组方法、生成固定范围内数组、生成随机数组,绘制指定均值和标准差正态分布图、均匀分布图绘制
    深度理解实分析:超越公式与算法的学习方法
    团队的Code Review实践
    HIVE优化:语句、参数、表结构优化
    【桶计数】面试题 01.02. 判定是否互为字符重排
  • 原文地址:https://blog.csdn.net/wangnaisheng/article/details/127766696