在《WCF 並發的本質》中,我們談到了WCF提供的三種不同的並發模式,使開發者可以根據具體的情況 選擇不同的並發處理的策略。對於這三種並發模式,Multiple采用的並行的執行方式,而Single和 Reentrant則是采用串行的執行方式。串行執行即同步執行,在WCF並發框架體系中,這樣的同步機制是如 何實現的呢?
一、Concurrency.Single模式下的同步實現
實際上,WCF並發框架體系下針對Concurrency.Single模式的實現非常簡單,其本質就是對 InstanceContext進行加鎖。如果采用反編譯工具查看InstanceContext的定義,你會發現 InstanceContext類中定義了一個類型為System.Object名為ThisLock的內部屬性,而該屬性實際上就是對 基類CommunicationObject同名屬性的引用,相關代碼入下所示。WCF就是通過對InstanceContext的 ThisLock進行加鎖,確保了對InstanceContext的同步訪問。
1: public sealed class InstanceContext : CommunicationObject, IExtensibleObject<InstanceContext>
2: {
3: //其它成員
4: internal object ThisLock
5: {
6: get
7: {
8: return base.ThisLock;
9: }
10: }
11: }
12: public abstract class CommunicationObject : ICommunicationObject
13: {
14: //其它成員
15: protected object ThisLock { get; }
16: }
具體來講,WCF服務端運行時在處理服務調用消息請求之後,利用實例上下文提供者 (InstanceContextProvider)創建新的或者獲取現有的InstanceContext。然後,WCF會將請求消息分發 給該InstanceContext對消息進行進一步處理。在處理操作執行之前,如果發現相應的服務采用的並發模 式是ConcurrencyMode.Single,WCF運行時會試圖獲取InstanceContext的ThisLock上的鎖,或者說後續的 操作進行再對InstanceContext的ThisLock鎖定的情況下執行的。這樣就保證了單一的InstanceContext對 象在ConcurrencyMode.Single並發模式下永遠是以同步的方式被調用的。
二、Concurrency.Reentrant模式下的同步實現
在ConcurrencyMode.Single並發模式下,從請求被WCF服務端運行時分發給相應的InstanceContext到 請求處理完成的整個過程中,InstanceContext被鎖定。如果在服務操作執行過程中涉及到對客戶端的回 調,並且回調操作采用請求/回復消息交換模式,當被WCF服務端運行時接收到從客戶端返回的回復消息後 ,會將請求消息再次分發給相同的InstanceContext。運行時分發回調回復消息與普通服務調用請求消息 采用相同的機制,同樣需要在對InstanceContext成功鎖定的情況下進行。很明顯,這樣產生了死鎖 (Deadlock)。
所以,如果在服務操作執行過程中需要對客戶端實施回調,要麼將采用單向(One-way)的方式進行回 調,要麼將服務的並發模式設置成ConcurrencyMode.Reentrant或者ConcurrencyMode.Multiple。否則, 如圖1所示的InvalidOperationException異常會在進行回調操作的時候拋出。從異常消息我們可以看出, VS的漢化真的不敢恭維,如果要正常理解異常消息的含義,你需要知道這裡的“郵件”、“可重輸入”和 “多個”是依次對“Message”、“Reentrant”和“Multiple”的翻譯。
圖1 在Single模式執行回調導致的異常
如果我們真的需要在服務操作過程中實施基於請求/回復模式的回調,毫無疑問采用 Concurrency.Multiple並發模式可以解決死鎖的問題,因為Concurrency.Multiple模式根本就是存在對 InstanceContext加鎖的問題。那麼,在 Concurrency.Reentrant模式下,WCF並發框架體系又是如何解決 這個問題的呢?
Reentrant,翻譯成漢語就是“重入”(VS將其翻譯成“重輸入”簡直莫名其妙),意思是服務操作過 程中完成了對外調用(Call Out)還能重新回到相應的位置繼續執行。同Concurrency.Single模式一樣, WCF運行時將調用請求消息分發給相應的InstanceContext之前,會先對其加鎖。但是,在開始實施回調的 之前,對InstanceContext的鎖定會被解除,當回調返回後再對其加鎖。
對於Concurrency.Reentrant有一點需要特別說明,當服務端進行回調時,由於加載InstanceContext 上的鎖會被釋放,意味著其它服務請求會被分發給該InstanceContext。當回調返回的時候,如果 InstanceContext正被用於才處理在進行回調過程抵達的請求,雖然自己是先來者,依然會等待,因為重 入後的InstanceContext被鎖定。如果等待的時間超過設定的超時時限,客戶端會拋出TimeoutException 異常。
由於WCF的並發是針對某個封裝了服務實例的InstanceContext而言的,所以在不同的實例上下文模式 下,會表現出不同的並發行為。在下一篇文章中,我將從具體的實例上下文模式的角度來剖析WCF的並發 ,敬請期待。