聊聊WPF中的Dispatcher

2016-08-20 10:35:57来源:http://muzizongheng.blog.51cto.com/856912/1332940作者:muzizongheng人点击


DispatcherObject,Dispatcher,Thread之间的关系
我们都知道WPF中的控件类都是从System.Windows.Threading.DispatcherObject继承而来, 而DispatcherObject又在构造时与当前线程的Dispatcher关联起来,CurrentDispatcher如果为null则会主动new一个Dispatcher并且在构造时和当前创建它的线程关联起来了。因此整个链为DispatcherObject <- Dispatcher <- Thread. 具体我们一起看看反编译的红色代码:
public abstract class DispatcherObject{private Dispatcher _dispatcher;
[EditorBrowsable(EditorBrowsableState.Advanced)]public Dispatcher Dispatcher{ get {return this ._dispatcher; }}
protected DispatcherObject(){ base ./u002Ector(); this ._dispatcher = Dispatcher.CurrentDispatcher;}
........................................................}
public sealed class Dispatcher{public static Dispatcher CurrentDispatcher{ get {return Dispatcher.FromThread(Thread.CurrentThread) ?? new Dispatcher(); }}
private Dispatcher(){............................. Dispatcher._tlsDispatcher = this ; this ._dispatcherThread = Thread.CurrentThread;.............................}
..............................}
这样设计的原则就保证:界面元素只有被创建它的线程访问

Dispatcher中Invoke,BeginInvoke和Win32中SendMessage,PostMessage的关系
上面我们提到wpf的界面元素只有被创建它的线程来访问,如果我们想在后台或者其他线程里该怎么办?
答案就是利用Dispatcher的Invoke和BeginInvoke,作用就是把委托放到界面元素关联的Dispatcher里的工作项里,然后此Dispatcher关联的线程进行执行。所不同的是Invoke是在关联的线程里同步执行委托, 而BeginInvoke是在关联的线程里异步执行委托。
做过Win32或者MFC编程的童鞋们都知道win32中有SendMessage和PostMessage类似的概念,这些概念有什么内在关系呢?
其实Dispatcher的Invoke和BeginInvoke都是调用Win32的PostMessage传递窗体句柄和消息号,反编译代码如下:private bool RequestForegroundProcessing(){ if (this._postedProcessingType >= 2)return true; if (this._postedProcessingType == 1)SafeNativeMethods.KillTimer(new HandleRef((object) this, this._window.Value.Handle), 1); this._postedProcessingType = 2; return MS.Win32.UnsafeNativeMethods.TryPostMessage(new HandleRef((object) this, this._window.Value.Handle), Dispatcher._msgProcessQueue, IntPtr.Zero, IntPtr.Zero);}
只不过Invoke在PostMessage后调用了内部返回值DispatcherOperation的wait方法,在执行结束后才返回。反编译代码如下:
DispatcherOperation dispatcherOperation = this.BeginInvokeImpl(priority, method, args, isSingleParameter);if (dispatcherOperation != null){ int num = (int) dispatcherOperation.Wait(timeout); if (dispatcherOperation.Status == DispatcherOperationStatus.Completed)obj = dispatcherOperation.Result; else if (dispatcherOperation.Status == DispatcherOperationStatus.Aborted)obj = (object) null; elsedispatcherOperation.Abort();}


WPF的消息泵和Win32的消息泵之间的关系
WPF的窗体程序都必须隐式或者显式调用Application.Run()来初始化WPF窗体。当Application.Run()调用时, 会在其内部调用Dispatcher.Run()方法。最终会在PushFrame()方法内初始化消息泵。
具体为:Application.Run() -> Dispatcher.Run() -> Dispatcher.PushFrame() -> Dispatcher.PushFrameImpl()private void PushFrameImpl(DispatcherFrame frame){ MSG msg = new MSG(); ++this._frameDepth; try {SynchronizationContext current = SynchronizationContext.Current;bool flag = current != this._dispatcherSynchronizationContext;try{ if (flag)SynchronizationContext.SetSynchronizationContext((SynchronizationContext) this._dispatcherSynchronizationContext); while (frame.Continue && this.GetMessage(ref msg, IntPtr.Zero, 0, 0))this.TranslateAndDispatchMessage(ref msg); if (this._frameDepth != 1 || !this._hasShutdownStarted)return; this.ShutdownImpl();}finally{ if (flag)SynchronizationContext.SetSynchronizationContext(current);} } finally {--this._frameDepth;if (this._frameDepth == 0) this._exitAllFrames = false; }}

Ok,从上述反编译的代码可以看到,WPF还是通过Dispatcher内部实现了传统的Win32消息循环, 如GetMessage,TranslateMessage,DispatchMessage。
下面是win32消息泵的实现, 大家可以对比下:
BOOL bRet;

while( (bRet = GetMessage( &msg, hWnd, 0, 0 )) != 0)
{
if (bRet == -1)
{
// handle the error and possibly exit
}
else
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}





最新文章

123

最新摄影

微信扫一扫

第七城市微信公众平台