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