天天看点

聊聊WPF中的Dispatcher

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;           else             dispatcherOperation.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);

}

继续阅读