几个月之前因为项目需要,需要实现一个类似于WPF Dispatcher类的类,来实现一些线程的调度。之前因为一直做Asp.Net,根本没有钻到这个层次去,做的过程中,诸多不顺,重构了四五次,终于实现,满足项目要求。 Dispatcher的源码对我来说,看的确实很累,各种累关联,不知所云。当时仅有的周永恒的博客看了六七遍也只是知道了大概的轮廓。今天我这里讲的,就是按照当时的项目需求的方向所理解和收获的一些知识,分享出来。太全面的东西,我讲出来只怕误人子弟了,大家还是去参照老周的博客吧。O(∩_∩)O~

一、Dispatcher几个重要的方法。

Dispatcher译名是调度者,他的工作就是不断的从线程的消息队列中取出一个消息,通过TranslateAndDispatchMessage派发出去,WndProcHook来处理捕获到的消息。

1.Run()

Run()是dispatcher的一个静态方法,这个方法在WPF应用程序运行起来之前,已经被调用了,可以通过调用堆栈来看到。

而Run()调用的是一个所谓的消息泵,---》PushFrame(new DispatcherFrame());---》PushFrameImpl(frame);

它的源码如下,关键的就是拿个while循环,通过GetMessage来不断的获取消息,然后派发出去。

 [SecurityCritical, SecurityTreatAsSafe ]
private void PushFrameImpl(DispatcherFrame frame)
{
MSG msg = new MSG();
//.............................
while(frame.Continue)
{
if (!GetMessage(ref msg, IntPtr.Zero, , ))
break; TranslateAndDispatchMessage(ref msg);
} // If this was the last frame to exit after a quit, we
//................. 略了一些代码
}

而这里的GetMessage和TranslateAndDispactchMessage 本质上都是调用的Win32的API函数。和GetMessage对应的还有个PeekMessage,前者一直等待有消息到才返回,后者获取一次就返回。上文的意思就是遇上错误或者窗口退出消息就中断。在WPF中还有一个隐藏的窗口,是窗口类,但没有窗体,专门来捕获派发过来的消息(工作线程中委Invoke或者BeginInvoke的方法也是包装成了消息)。在Dispatcher的构造函数中:

           MessageOnlyHwndWrapper window = new MessageOnlyHwndWrapper();
_window = new SecurityCriticalData<MessageOnlyHwndWrapper>( window );
_hook = new HwndWrapperHook(WndProcHook);
_window.Value.AddHook(_hook);

而在WndProcHook 方法中,主要的就是处理消息。而其中又调用了ProcessQueue() 这个方法。

2. ProcessQueue();

在ProcessQueue中,主要是处理BeginInvoke和Invoke 进入队列的任务(或者说操作)。关键代码如下,从队列中 dequeue()出来后,invoke执行了。而这里的DispatcherOperation就包含了我们之前Invoke或者BeginInvoke进来的操作,到这里就是正真被执行了。各个版本的源码会有些出入,下载安装的.net4.0源码和反编译出来的源码是有些出入的。

  private void ProcessQueue()
{
DispatcherPriority maxPriority = DispatcherPriority.Invalid; // NOTE: should be Priority.Invalid
DispatcherOperation op = null;
DispatcherHooks hooks = null;
//..............
{
op = _queue.Dequeue();
hooks = _hooks;
}
// .........
op.Invoke(); //..........
}

3.BeginInvokeImpl()

上面好像有点倒叙,讲了出队列,没有说入队列。 我们在工作线程调用的方法,如下

   private void ButtonOnclick(object sender, EventArgs e)
{
Button.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() =>
{
Button.Content = _random.NextDouble();//显示一个随机值。
}));
}

BeginInvoke和Invoke,Dispatcher都重载了多个版本,Invoke的最终调用的是InvokeImpl,BeginInvoke最终调用的是BeginInvokeImpl,而InvokeImpl内部还是调用的BeginInvokeImpl。 在这个方法中关键代码如下:

  [FriendAccessAllowed] //used by DispatcherExtensionMethods in System.Windows.Presentation.dll
internal DispatcherOperation BeginInvokeImpl(DispatcherPriority priority, Delegate method, object args, int numArgs)
{
ValidatePriority(priority, "priority");
if(method == null)
{
throw new ArgumentNullException("method");
} DispatcherOperation operation = null;
DispatcherHooks hooks = null;
bool succeeded = false; // Could be a non-dispatcher thread, lock to read
lock(_instanceLock)
{
if (!_hasShutdownFinished && !Environment.HasShutdownStarted)
{
operation = new DispatcherOperation(this, method, priority, args, numArgs); // Add the operation to the work queue
operation._item = _queue.Enqueue(priority, operation); // Make sure we will wake up to process this operation.
succeeded = RequestProcessing(); if (succeeded)
{
hooks = _hooks;
}
else
{
// Dequeue and abort the item. We can safely return this to the user.
_queue.RemoveItem(operation._item);
operation._status = DispatcherOperationStatus.Aborted;
}
}
else
{
// Rather than returning null we'll create an aborted operation and return it to the user
operation = new DispatcherOperation(this, method, priority);
}
} // .................return operation;
}

通过Enqueue 进入了消息队列。而这里的_queue的定义是 :

  private PriorityQueue<DispatcherOperation> _queue;

就是一个带有优先级的操作队列。

二、实现一个小的Dispatcher

找到了上面几个重要点,就可以构建自己的调度者了。(不得不说一下,刚打死了一直小老鼠。这么恶劣的公司宿舍,点燃一支烟,压压惊,继续写代码,不容易啊)

1.自己的RUN()

  [SecurityCritical, UIPermission(SecurityAction.LinkDemand, Unrestricted = true)]
public void Run()
{
var waitHandles = new WaitHandle[];// 这里是项目需要的两个handle,  _stopEvent = new AutoResetEvent(false); 专门处理程序窗口停止。
waitHandles[] = _stopEvent;
waitHandles[] = _triggerEvent; var msg = new MSG();
while (!_isShutdown)
{
var index = MsgWaitForMultipleObjects(waitHandles, false, Infinite, 0x04BF);//等待事件函数,他能等待到是否触发了waitHandles中的handle 其他的消息交给peekmessage处理
switch (index)
{
case :
_isShutdown = true;
_stopEvent.Reset();
break;
case :
_triggerEvent.Reset();
while (TaskQueueCount > )
{
ProcessQueue();//处理invoke和beginInvoke。
}
break;
default:
while (PeekMessage(ref msg))
{
TranslateAndDispatchMessage(ref msg);
}
break;
}
}
}
MsgWaitForMultipleObjects:
  private int MsgWaitForMultipleObjects(WaitHandle[] wHandles, bool fWaitAll, int dwMilliseconds, int dwWakeMask)
{
var i = wHandles.Length;
var intPtrs = new IntPtr[i];
for (int j = ; j < i; j++)
{
intPtrs[j] = wHandles[j].SafeWaitHandle.DangerousGetHandle();//这个转换的方法,找了两天。不容易啊。handle转化为inPtrs
}
return UnsafeNativeMethods.MsgWaitForMultipleObjects(i, intPtrs, fWaitAll, dwMilliseconds, dwWakeMask);//这个方法可以去查MSDN,也是个WIN32函数,下面的UnsafeNativeMethods中有写。
}

ProcessQueue

 public void ProcessQueue()
{
DispatcherOperation operation = null;
var invalid = _queue.MaxPriority;
if (((invalid != DispatcherPriority.Invalid) && (invalid != DispatcherPriority.Inactive)) || _queue.Count == )
{
operation = _queue.Dequeue();
}
if (operation != null)
{
operation.Invoke();
operation.InvokeCompletions();
}
}

TranslateAndDispatchMessage 和 PeekMessage

[SecurityCritical]
private bool PeekMessage(ref MSG msg)
{
var nullHandleRef = new HandleRef(null, IntPtr.Zero);
return UnsafeNativeMethods.PeekMessage(ref msg, nullHandleRef, , , );
}
[SecurityCritical]
private void TranslateAndDispatchMessage(ref MSG msg)
{
bool handled = ComponentDispatcher.RaiseThreadMessage(ref msg); if (!handled)
{
UnsafeNativeMethods.TranslateMessage(ref msg);
UnsafeNativeMethods.DispatchMessage(ref msg);
}
}

UnsafeNativeMethods精简了很多。

 public class UnsafeNativeMethods
{
[SuppressUnmanagedCodeSecurity, SecurityCritical, DllImport("user32.dll", EntryPoint = "GetMessageW", CharSet = CharSet.Unicode, SetLastError = true, ExactSpelling = true)]
private static extern int IntGetMessageW([In, Out] ref MSG msg, HandleRef hWnd, int uMsgFilterMin, int uMsgFilterMax); /// <summary>
/// TranslateMessage
/// </summary>
/// <param name="msg"></param>
/// <returns></returns>
[SecurityCritical, SuppressUnmanagedCodeSecurity, DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
internal static extern bool TranslateMessage([In, Out] ref MSG msg); /// <summary>
/// DispatchMessage
/// </summary>
/// <param name="msg"></param>
/// <returns></returns>
[SecurityCritical, SuppressUnmanagedCodeSecurity, DllImport("user32.dll", CharSet = CharSet.Auto)]
internal static extern IntPtr DispatchMessage([In] ref MSG msg); [SuppressUnmanagedCodeSecurity, SecurityCritical, DllImport("user32.dll", EntryPoint = "MsgWaitForMultipleObjectsEx", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true)]
private static extern int IntMsgWaitForMultipleObjectsEx(int nCount, IntPtr[] pHandles, int dwMilliseconds, int dwWakeMask, int dwFlags); /// <summary>
/// 等待消息和事件
/// </summary>
/// <param name="nCount"></param>
/// <param name="pHandles"></param>
/// <param name="fWaitAll"></param>
/// <param name="dwMilliseconds"></param>
/// <param name="dwWakeMask"></param>
/// <returns></returns>
[SuppressUnmanagedCodeSecurity, SecurityCritical, DllImport("user32.dll", EntryPoint = "MsgWaitForMultipleObjects", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true)]
internal static extern int MsgWaitForMultipleObjects(int nCount, IntPtr[] pHandles, bool fWaitAll, int dwMilliseconds, int dwWakeMask); /// <summary>
/// PeekMessage
/// </summary>
/// <param name="msg"></param>
/// <param name="hwnd"></param>
/// <param name="msgMin"></param>
/// <param name="msgMax"></param>
/// <param name="remove"></param>
/// <returns></returns>
[SecurityCritical, SuppressUnmanagedCodeSecurity, DllImport("user32.dll", CharSet = CharSet.Auto)]
internal static extern bool PeekMessage([In, Out] ref MSG msg, HandleRef hwnd, int msgMin, int msgMax, int remove); [ComImport, SecurityCritical, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("8f1b8ad8-0b6b-4874-90c5-bd76011e8f7c"), SuppressUnmanagedCodeSecurity]
internal interface ITfMessagePump
{
[SecurityCritical]
void PeekMessageA(ref MSG msg, IntPtr hwnd, int msgFilterMin, int msgFilterMax, int removeMsg, out int result);
[SecurityCritical]
void GetMessageA(ref MSG msg, IntPtr hwnd, int msgFilterMin, int msgFilterMax, out int result);
[SecurityCritical]
void PeekMessageW(ref MSG msg, IntPtr hwnd, int msgFilterMin, int msgFilterMax, int removeMsg, out int result);
[SecurityCritical]
void GetMessageW(ref MSG msg, IntPtr hwnd, int msgFilterMin, int msgFilterMax, out int result);
} /// <summary>
/// 获取消息
/// </summary>
/// <param name="msg"></param>
/// <param name="hWnd"></param>
/// <param name="uMsgFilterMin"></param>
/// <param name="uMsgFilterMax"></param>
/// <returns></returns>
/// <exception cref="Win32Exception"></exception>
[SecurityCritical]
public static bool GetMessageW([In, Out] ref MSG msg, HandleRef hWnd, int uMsgFilterMin, int uMsgFilterMax)
{
var index = IntGetMessageW(ref msg, hWnd, uMsgFilterMin, uMsgFilterMax);
switch (index)
{
case -:
throw new Win32Exception(); case :
return false;
}
return true;
} [SecurityCritical]
internal static int MsgWaitForMultipleObjectsEx(int nCount, IntPtr[] pHandles, int dwMilliseconds, int dwWakeMask, int dwFlags)
{
int num = IntMsgWaitForMultipleObjectsEx(nCount, pHandles, dwMilliseconds, dwWakeMask, dwFlags);
if (num == -)
{
throw new Win32Exception();
}
return num;
}
}

其他的类,差不多都是拿来主义。还有一些设计公司内部的东西,不便贴出来,但核心的东西都说了。

三、调用

1.我们可以把操作压入自己的dispatcher中。

  Dispather.BeginInvoke(new Action<object>(TriggerAction), DispatcherPriority.Normal, );

2.让timer跑起来

      public MainWindow()
{
InitializeComponent();
_thread = new Thread(Run);
_thread.Start();
var id = Thread.CurrentThread.ManagedThreadId;
} [SecurityCritical, UIPermission(SecurityAction.LinkDemand, Unrestricted = true)]
private void Run()
{
var mytimer = new System.Windows.Forms.Timer();//windows form中的timer 必须依赖一个窗体线程,如果没有这个下面的_dispatcher.Run() tick中的方法 是不会有效果的。
            mytimer.Tick += ButtonOnclick;
mytimer.Enabled = true;
mytimer.Interval = ;
//var id = Thread.CurrentThread.ManagedThreadId;
_dispatcher.Run();//这是必须的 这里纯粹只是一个例子 在这里没有使用意义。
}

结语:学习源码确实有很多收获,但是确实很累,要是没有别人指点,我几乎是不可能做出来的,特别是对核心代码的提炼,很难分清,重要的一点就是要明白需求。之前也没有接触过底层的函数,可能做C++的人接触的比较多,像windows的消息机制,这里理解起来就比我要快的多了。在这里分享出来,与君共勉!如果对你有帮助,就支持一个吧。

写完11点多了,洗洗睡了。园友们晚安!

WPF Dispatcher 一次小重构的更多相关文章

  1. 深入了解 WPF Dispatcher 的工作原理(PushFrame 部分)

    在上一篇文章 深入了解 WPF Dispatcher 的工作原理(Invoke/InvokeAsync 部分) 中我们发现 Dispatcher.Invoke 方法内部是靠 Dispatcher.Pu ...

  2. 深入了解 WPF Dispatcher 的工作原理(Invoke/InvokeAsync 部分)

    深耕 WPF 开发的各位程序员大大们一定避不开使用 Dispatcher.跨线程访问 UI 当然免不了用到它,将某个任务延迟到当前任务之后执行也会用到它.Dispatcher.Invoke.Dispa ...

  3. WPF Dispatcher 频繁调度导致的性能问题

    问题 WPF Dispatcher 提供了UI线程之外的线程异步操作(请求)UI变化.一次Invoke/BeginInvoke调用产生一个DispatcherOperation,将挂在调度队列中,按照 ...

  4. WPF数据爬取小工具-某宝推广位批量生成,及订单爬取 记:接单最痛一次的感悟

    项目由来:上月闲来无事接到接到一个单子,自动登录 X宝平台,然后重定向到指定页面批量生成推广位信息:与此同时自动定时同步订单数据到需求提供方的Java服务. 当然期间遇到一个小小的问题就是界面样式的问 ...

  5. SkiaSharp 之 WPF 自绘 投篮小游戏(案例版)

    此案例主要是针对光线投影法碰撞检测功能的示例,顺便做成了一个小游戏,很简单,但是,效果却很不错. 投篮小游戏 规则,点击投篮目标点,就会有一个球沿着相关抛物线,然后,判断是否进入篮子里,其实就是一个矩 ...

  6. rails小重构:将图片加入产品Model之二

    在前面我们重构了product中图片的实现,但是还是有一些小问题.比如用户如果上传一个非图片格式的文件时的验证情况. 我们之前是将图片格式验证代码放在Picture类中: validates_form ...

  7. WPF Dispatcher介绍

    微软在WPF引入了Dispatcher,那么这个Dispatcher的主要作用是什么呢?Dispatcher的作用是用于管理线程工作项队列.主线程负责接收输入.处理事件.绘制屏幕等工作,这样一来,UI ...

  8. WPF Dispatcher的使用

     <Window x:Class="DispatcherExam.MainWindow"         xmlns="http://schemas.micro ...

  9. WPF Dispatcher使用

    微软在WPF引入了Dispatcher,那么这个Dispatcher的主要作用是什么呢?Dispatcher的作用是用于管理线程工作项队列.主线程负责接收输入.处理事件.绘制屏幕等工作,这样一来,UI ...

随机推荐

  1. mysql 主从复制 实践

    异步主从复制   主从部署步骤: 备份还原 使用mysqldump或者xtrabackup 把主库现有基础数据还原到从库 授权 grant replication slave on *.* 给从库一个 ...

  2. Python 之Django

    1.安装Django(下载慢的时候用MaxVPN) pip3 install django 2.Django处理流程 新建project(mysite),执行dj(mysite),然后在console ...

  3. 第一零四天上课 PHP TP框架下的文件上传

    控制器代码(TestController.class.php) <?php namespace Home\Controller; use Home\Controller\EmptyControl ...

  4. ORACLE 数据的逻辑组成

    数据块(block) Oracle数据块(Data Block)是一组连续的操作系统块.分配数据库块大小是在Oracle数据库创建时设置的,数据块是Oracle读写的基本单位.数据块的大小一般是操作系 ...

  5. [转] Python包和类的基本用法

    http://blog.csdn.net/liukang325/article/details/46724365 建立一个文件夹filePackage 在filePackage 文件夹内创建 __in ...

  6. 【BZOJ2595】游览计划(状压DP,斯坦纳树)

    题意:见题面(我发现自己真是越来越懒了) 有N*M的矩阵,每个格子有一个值a[i,j] 现要求将其中的K个点(称为关键点)用格子连接起来,取(i,j)的费用就是a[i,j] 求K点全部连通的最小花费以 ...

  7. SAP 如何查看用户登录信息

    1.首先进入事务代码 SM19  配置审计参数文件 2.选择客户端,用户名,并且勾选过滤激活之后点击细节配置,进入如下界面: 配置完成之后,点击保存. 3.并且可以进入SM20界面,选择要查看的客户端 ...

  8. JS中delete删除对象属性

    1.删除对象属性 function fun(){   this.name = 'mm';   }   var obj = new fun();   console.log(obj.name);//mm ...

  9. Sql Server 孤立用户解决办法

    Sql Server 孤立用户 是我们经常遇到的事情,今天详细的梳理了下,希望能帮到你 当把用户数据库从一台 Sql Server 使用备份和恢复的方式迁移到另一台服务器.数据库恢复以后,原先用户定义 ...

  10. Android消息机制:Looper,MessageQueue,Message与handler

    Android消息机制好多人都讲过,但是自己去翻源码的时候才能明白. 今天试着讲一下,因为目标是讲清楚整体逻辑,所以不追究细节. Message是消息机制的核心,所以从Message讲起. 1.Mes ...