ExecutionContext & SynchronizationContext


最近在研究.net4.5中的有關異步編程的新特性,從自己Google Reader 訂閱的一些博客中接觸到了兩個很陌生的單詞ExecutionContext和SynchronizationContext,於是仔細研究了一下,記錄下來備忘。
什么是ExecutionContext
在許多系統中thread-local storage(TLS)線程本地存儲記錄了正在運行的當前環境或者上下文的一些信息,而在CLR中ExecutionContext做了類似的事情.在同步的世界中,所有的一切都發生在當前線程內,線程內有關的所有數據對其內所有代碼可見,也同時被所有代碼所操縱影響.也就是說假設有方法ABC在當前線程內順序執行,當前線程環境的一切信息對ABC都是可見的,同時ABC又影響着這些當前線程環境.但是在異步的世界情況變了,還假設有方法ABC,這三個方法有可能運行在不同的線程中,ABC三者是相互見不到對方線程信息的,所以需要在每個異步點傳遞當前線程環境信息到另一個線程中,另一個線程中的方法才能訪問該線程中數據信息並且修改這些信息.完成這個任務就需要ExecutionContext記錄當前線程環境然后再另一個線程中restore出來. 情況類似於這樣:

// 記錄當前的環境信息到ec, 這些信息會在委托調用時通過靜態的方法Run恢復出來
ExecutionContext ec = ExecutionContext.Capture(); 


ExecutionContext.Run(ec, delegate 
{ 
… // 在這的代碼就能看見ec中的環境信息了。
}, null);

在.Net FrameWork中所有處理異步情況的方法都會通過類似的方法Capture/Restore ExecutionContext(不包括unsafe代碼),比如Task.Run,ThreadPool.QueueUserWorkItem,Delegate.BeginInvoke,Stream.BeginRead,DispatcherSynchronizationContext.Post 

比如Task.Run,當我們調用Run時,背后所做的事就是ExecutionContext.Capture()捕獲當前線程環境,然后在Run中的delegate方法執行的時候把環境還原。

什么是SynchronizationContext

程序員都很熱衷與抽象,對硬編碼深惡痛絕,分層和抽象會為我們日后維護和擴展系統帶來極大的便利,這也就是為什么會有接口,抽象類,和虛方法。而SynchronizationContext也是一種抽象。打個比方,當我們編寫WinForm程序時,為避免后台處理海量數據帶來的UI界面假死狀態,通常會重開線程或者將這些工作放入線程池等,任務完成之后把處理的結果封送返回給UI線程時,通常會用Control.BeginInvoke方法。但是這種情況如果發生在WPF和Silverlight程序中呢,封送處理又得用Dispatcher.BeginInvoke或者InvokeAsync.ASP.Net又是另一種情況。於是我們使用了不同了方法完成了相同的動作,那么對於不同的UI框架我們怎么處理這些相同事件呢?於是抽象的想法來了,這就是SynchronizationContext。它提供了一個虛方法Post(),這個方法會接收委托方法並且執行它。而對於WinForm的情況會提供WinFormSynchronizationContext,它會覆蓋掉虛方法Post(),並且在子類中Post()調用Control.BeginInvoke;對於WPF或者Silverlight,會提供子類DispatcherSynchronizationContext,同樣覆蓋掉虛方法Post(),並且在子類Post中調用Dispatcher.Run。

public static void DoWork(Control c) 
{ 
ThreadPool.QueueUserWorkItem(delegate 
    { 
..... c.BeginInvoke(delegate { .....//對UI的操作 }); }); } 如果替換成SynchronizationContext去寫就成了這樣: public static void DoWork(SynchronizationContext sc) { ThreadPool.QueueUserWorkItem(delegate { … // do work on ThreadPool sc.Post(delegate { … // do work on UI }, null); }); }
或者如下
public static void DoWork() { var sc = SynchronizationContext.Current; ThreadPool.QueueUserWorkItem(delegate { … // do work on ThreadPool sc.Post(delegate { … // do work on the original context }, null); }); }

 

ExecutionContext VS SynchronizationContext

對比兩者你會發現它們都會在異步操作時捕獲當前線程環境上下文,但后續操作不一樣,ExecutionContext會調用delegate方法並在執行時在另一個線程中恢復該線程環境狀態。而SynchronizationContext僅僅是用當前捕獲的環境信息去調用委托方法,這個委托方法怎么什么時候執行,在哪執行,怎么執行都取決於底層的具體實現。

注意:

 以下CodeProject文章中有一點講得不太准確,根據慣例,如果一個線程的當前 SynchronizationContext 為 null,那么它隱式具有一個默認 SynchronizationContext。默認 SynchronizationContext 將其異步委托列隊到 ThreadPool,但在調用線程上直接執行其同步委托。因此,其上下文包含所有 ThreadPool 線程以及調用 Send 的任何線程。此上下文“借用”調用 Send 的線程,將它們放入其上下文,直至委托完成。從這種意義上講,默認上下文可以包含進程中的所有 線程。所以UI應用程序通常有兩個同步上下文,一個是UI線程的SynchronizationContext,另一個是包含ThreadPool線程的默認SynchronizationContext。

 

一般遇到疑問,首先去百度國內論壇,但是繼而回去CodeProject相關文章去佐證,好多文章都是從Codeproject上翻譯或者濃縮出來的,但這次事件我才發現CodeProject上文章也不靠譜,雖然那上邊有好多微軟MVP和其他大公司的技術人員,微軟的東西還是得去MSDN blog或者 MSDN Magzine上比較靠譜,尤其是看到Anders Hejlsberg, Stephen Toub, Erick Lippert, Herb Sutter審閱,就知道這是質量的保證。備忘完畢,還有半天時間,接着去深挖這塊東西。

 

reference:
http://blogs.msdn.com/b/pfxteam/archive/2012/06/15/executioncontext-vs-synchronizationcontext.aspx
http://www.codeproject.com/Articles/31971/Understanding-SynchronizationContext-Part-I
http://msdn.microsoft.com/zh-cn/magazine/gg598924.aspx
http://msdn.microsoft.com/zh-cn/magazine/hh456402.aspx


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM