聊聊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);
    }
}





發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章