通过实例理解WPF的Dispatcher

2024-02-08 11:32
文章标签 理解 实例 wpf dispatcher

本文主要是介绍通过实例理解WPF的Dispatcher,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Dispatcher提供用于管理线程工作项队列的服务。可以理解为消息队列,只是其中保存的是委托,而不是简单的windows消息。Dispatcher通常用来使我们的程序界面对于用户的操作响应更加迅速,通常用来更新UI,例如一个进度条。例如一个耗时操作,我们不想让使用者等得太着急,于是我们想显示一个进度条。

最直接的方法可能是在一个循环中更新,如以下这个错误的代码

ProgressBar1.Minimum = 0
 ProgressBar1.Maximum = short.MaxValue; //模拟一个耗时计算
 ProgressBar1.Value = 0
 while (ProgressBar1.Value < ProgressBar1.Maximum) 
 { 
 this.ProgressBar1.Value += 1
 }

然而直到循环结束,我们的界面都得不到更新,处于停止响应状态。我们利用Dispatcher成功的解决了这个问题: 

ProgressBar1.Minimum = 0
 ProgressBar1.Maximum = short.MaxValue; 
 ProgressBar1.Value = 0
 while (ProgressBar1.Value < ProgressBar1.Maximum) 
 { 
 Dispatcher.Invoke(new Action(() =>
 { 
 this.ProgressBar1.Value += 1
 }),System.Windows.Threading.DispatcherPriority.Background); 
 }

参考WPF的线程模型知道,WPF使用呈现(render)线程隐藏在后台负责应用程序的呈现,也就是界面的更新;而UI线程负责管理UI,上面的代码就运行在UI线程中,何谓管理UI,我的理解是可以改变UI元素的属性,例如ProgressBar的Value值,也就是可以操作UI元素的意思。遗憾的是我们的UI线程不能通过代码直接调用呈现线程让其更新界面。

我们该怎么理解以上的代码呢?Dispatcher.Invoke的作用是将一个委托消息加入到消息队列中,这个消息队列中的消息是有优先级的,优先级是由DispatcherPriority这个枚举确定的。同时Invoke是一个同步调用,因此是否也是通知Render线程开始刷新我们的界面呢?我们的while其实是在等待Render线程执行完毕后,才继续执行,这令我们想到了线程的同步机制。而代码1的情况,因为没有给Render线程任何通知,也就难怪不刷新了。由此得到的结论就是:Render线程何时开始工作其实是基于消息队列中的特殊消息的,例如我们的Dispatacher.Invoke产生的消息(肯定不是所有的消息,因为你动一下鼠标,界面不会刷新)。DispatcherPriority.Background指示我们的消息的优先级低于Normal,这样我们的UI线程就能停下来,让Render线程的消息优先得到执行。那照你这么说,与我们Dispatcher中排队的是什么样的委托没有关系了?只是起到一个UI线程暂停,同时通知Render线程工作的目的?是的,下面的代码能够到达同样的效果:

ProgressBar1.Minimum = 0
 ProgressBar1.Maximum = short.MaxValue; 
 ProgressBar1.Value = 0
 while (ProgressBar1.Value < ProgressBar1.Maximum) 
 { 
 this.ProgressBar1.Value += 1
 Dispatcher.Invoke(new Action(() =>
 { 
         }),System.Windows.Threading.DispatcherPriority.Background); 
 }

我们排队了一个空委托,照样能唤醒Render线程。

我觉得这样的理解已经足够,没有必要请出Windbg,继续深挖下去(其实,那不是我所擅长的领域:))。这个推测足以帮助我们改进代码,怎么,需要改进吗?如果我们的推测正确的话,那么,以上代码虽然使我们的用户界面得到了及时的刷新,但同时也降低了性能。原因很简单:频繁的线程切换。UI线程和Render线程在频繁的切换,以换取界面的及时刷新。short.MaxValue其实只有32767,做一个3万多次的循环加1,却用了20多秒!你不觉得惊讶吗? 改进方法也很简单:减少Dispatcher.Invoke的调用,降低线程切换频率。如下代码只需要352毫秒:

ProgressBar1.Minimum = 0
 ProgressBar1.Maximum = short.MaxValue ; 
 ProgressBar1.Value = 0
 double m = short.MaxValue / ProgressBar1.ActualWidth; 
 int n = 0
 while (ProgressBar1.Value < ProgressBar1.Maximum) 
 { 
 this.ProgressBar1.Value += 1
 n += 1
 if (n >= m) 
 { 
 Dispatcher.Invoke(new Action(() =>
 { 
 }), System.Windows.Threading.DispatcherPriority.Background); 
 n = 0
 } 
       }

这是多少倍的性能提升啊!同时也证明了我们的推测是正确的!看来,有时遇到不理解的问题,编写测试代码来验证一下是一个不错的选择。 

这篇关于通过实例理解WPF的Dispatcher的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/690892

相关文章

Python实例题之pygame开发打飞机游戏实例代码

《Python实例题之pygame开发打飞机游戏实例代码》对于python的学习者,能够写出一个飞机大战的程序代码,是不是感觉到非常的开心,:本文主要介绍Python实例题之pygame开发打飞机... 目录题目pygame-aircraft-game使用 Pygame 开发的打飞机游戏脚本代码解释初始化部

Spring组件实例化扩展点之InstantiationAwareBeanPostProcessor使用场景解析

《Spring组件实例化扩展点之InstantiationAwareBeanPostProcessor使用场景解析》InstantiationAwareBeanPostProcessor是Spring... 目录一、什么是InstantiationAwareBeanPostProcessor?二、核心方法解

java String.join()方法实例详解

《javaString.join()方法实例详解》String.join()是Java提供的一个实用方法,用于将多个字符串按照指定的分隔符连接成一个字符串,这一方法是Java8中引入的,极大地简化了... 目录bVARxMJava String.join() 方法详解1. 方法定义2. 基本用法2.1 拼接

Linux lvm实例之如何创建一个专用于MySQL数据存储的LVM卷组

《Linuxlvm实例之如何创建一个专用于MySQL数据存储的LVM卷组》:本文主要介绍使用Linux创建一个专用于MySQL数据存储的LVM卷组的实例,具有很好的参考价值,希望对大家有所帮助,... 目录在Centos 7上创建卷China编程组并配置mysql数据目录1. 检查现有磁盘2. 创建物理卷3. 创

Java List排序实例代码详解

《JavaList排序实例代码详解》:本文主要介绍JavaList排序的相关资料,Java排序方法包括自然排序、自定义排序、Lambda简化及多条件排序,实现灵活且代码简洁,文中通过代码介绍的... 目录一、自然排序二、自定义排序规则三、使用 Lambda 表达式简化 Comparator四、多条件排序五、

Java实例化对象的​7种方式详解

《Java实例化对象的​7种方式详解》在Java中,实例化对象的方式有多种,具体取决于场景需求和设计模式,本文整理了7种常用的方法,文中的示例代码讲解详细,有需要的可以了解下... 目录1. ​new 关键字(直接构造)​2. ​反射(Reflection)​​3. ​克隆(Clone)​​4. ​反序列化

使用WPF实现窗口抖动动画效果

《使用WPF实现窗口抖动动画效果》在用户界面设计中,适当的动画反馈可以提升用户体验,尤其是在错误提示、操作失败等场景下,窗口抖动作为一种常见且直观的视觉反馈方式,常用于提醒用户注意当前状态,本文将详细... 目录前言实现思路概述核心代码实现1、 获取目标窗口2、初始化基础位置值3、创建抖动动画4、动画完成后

Python解决雅努斯问题实例方案详解

《Python解决雅努斯问题实例方案详解》:本文主要介绍Python解决雅努斯问题实例方案,雅努斯问题是指AI生成的3D对象在不同视角下出现不一致性的问题,即从不同角度看物体时,物体的形状会出现不... 目录一、雅努斯简介二、雅努斯问题三、示例代码四、解决方案五、完整解决方案一、雅努斯简介雅努斯(Janu

spring IOC的理解之原理和实现过程

《springIOC的理解之原理和实现过程》:本文主要介绍springIOC的理解之原理和实现过程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、IoC 核心概念二、核心原理1. 容器架构2. 核心组件3. 工作流程三、关键实现机制1. Bean生命周期2.

Python开发文字版随机事件游戏的项目实例

《Python开发文字版随机事件游戏的项目实例》随机事件游戏是一种通过生成不可预测的事件来增强游戏体验的类型,在这篇博文中,我们将使用Python开发一款文字版随机事件游戏,通过这个项目,读者不仅能够... 目录项目概述2.1 游戏概念2.2 游戏特色2.3 目标玩家群体技术选择与环境准备3.1 开发环境3