傳入的客戶端調用消息會分發給Windows I/O線程池(線程默認爲1000)上的服務實例。多個客戶端能夠發起多個併發的調用,而且服務能夠在多個線程上處理這些請求。若是傳入的調用分發給同一個服務實例,就必須對內存中的服務狀態提供線程安全的訪問方式,不然,可能存在錯誤的風險。客戶端回調時的內存狀態亦是如此,由於回調也是分發給I/O線程池上的線程。此外,爲了同步訪問實例的狀態,還須要同步訪問實例之間共享的資源,好比靜態變量。安全
WCF提供了兩種同步模式:自動同步與手動同步。雖然自動同步會指導WCF去同步訪問服務實例,可是它只對服務和回調類可用。手工同步是讓開發者來控制同步,須要應用程序具體的集成工做。開發者須要使用.NET同步鎖。手工同步的優勢是,對服務類和非服務類來講,它均可以用,並且它容許開發者去優化吞吐量和伸縮性。多線程
服務實例線程安全性與服務實例化模式緊密相關。併發
根據定義,單調服務實例是安全的,由於每一個調用都有本身的服務實例。實例只分配給本身的工做線程訪問,沒必要考慮同步問題。可是,單調服務是狀態可知的,能夠將狀態存儲在內部資源裏,好比靜態字典裏,能夠被多個線程訪問,由於服務可使用併發調用,不管是來自相同的客戶端仍是不一樣的客戶端。優化
會話服務須要併發管理和同步,由於客戶端可能使用相同的代理或者經過多線程發送請求給服務。spa
對於併發訪問單例服務纔是最敏感,並且它必需要支持同步訪問。單例服務有一些全部客戶端共享的狀態信息。線程
發生客戶端多線程調用的最大多是,會話和單例中有多個客戶端在不一樣的執行上下文裏運行,每一個客戶端都使用本身的線程去調用服務。代理
但全部的調用都會經過I/O線程池上的線程進入相同的服務實例中——本質上,這就須要同步機制。code
ServiceBehavior的ConcurrencyMode屬性控制着服務實例的併發訪問。對象
// 指定服務類是支持單線程仍是多線程操做模式。 public enum ConcurrencyMode { // 默認,單線程模型 Single = 0, // 單線程,可重入模型,一般用在CallBack調用模型中 Reentrant = 1, // 多線程模型 Multiple = 2, } [AttributeUsage(AttributeTargets.Class)] public sealed class ServiceBehaviorAttribute : ... { public ConcurrencyMode ConcurrencyMode {get;set;} //More members }枚舉值ConcurrencyMode控制着是否以及什麼時候容許併發調用。ConcurrencyMode的名字是錯誤的,正確的名字應該是ConcurrencyContextMode,由於它同步訪問的不是實例,而是包含實例的上下文(InstanceContextMode也控制這上下文的實例化,而不是實例)。blog
一、ConcurrencyMode.Single
當併發模式配置爲ConcurrencyMode.Single時,WCF會自動對服務上下文提供同步機制,會使用同步鎖來把上下文和服務實例關聯起來以阻止併發調用。每次進入的調用必須得到同步鎖。一旦操做返回,WCF就會釋放同步鎖,從而容許後來的調用者進入。
同一個服務實例不會同時處理多個請求。當服務在處理請求時會對當前服務加鎖,若是再有其它請求須要該服務處理的時候,須要排隊等候。
例如:咱們去銀行去辦理業務,若是營業廳中只有一個窗口對外服務的話,那當前窗口每次只能處理一個用戶請求,若是再有其它用戶須要辦理業務的話,只能排隊等待。直到我辦理完業務後,營業窗口才能爲隊列中下個用戶提供服務。
二、ConcurrencyMode.Reentrant
可重入的單線程處理模式,它仍然是單線程處理。服務端一次仍然只能處理一個請求,若是有多個請求同時到達仍然須要排隊。與單線程不一樣的是,請求在處理過程當中能夠去調用其它服務,等到其它服務處理完成後,再回到原服務等待隊列尾排隊。在調用其它服務的過程當中,會暫時釋放鎖,其它等待線程會趁機進行服務的調用。這種模式常見於服務端回調客戶端的場境中。
重入適用於如下狀況:
- 若是單例服務調用的服務回調服務實例,那麼單例服務調出就會帶來死鎖問題。
- 在同一個應用程序域中,若是客戶端把代理存儲咋全局變量中,那麼服務調用校友的某些對象就須要使用代理引用,去回調最初的服務。
- 在非單向操做上的調用必須容許重入服務調用
- 若是服務調用執行的事件過長,設置不會重入服務實例,那麼要想辦法優化吞吐量。在調出期間,容許其它客戶端進入服務實例。
例如:咱們去銀行辦理業務,營業廳中仍是隻有一個窗口對外服務,一次只能處理一個用戶請求。我向銀行服務員請求辦理開戶業務,今後刻開始該服務被我鎖定,其它人員只能排隊等待。在服務員辦理業務的過程當中,會讓我填寫開戶申請表,這個簽字的過程就是服務端對客戶端的回調過程。因爲填寫開戶申請表的時間會很長,爲了避免耽擱後面排隊顧客的時間,我暫時釋放對服務員的鎖定,到旁邊填寫申請表,讓後面的人員辦理業務。在我簽完字後再回到隊列中等待服務,當再輪到個人時候我再次鎖定服務,把憑據交給服務員,讓他繼續處理個人業務,這至關於服務的「重入」過程。 等到業務辦完後,我會離開銀行櫃檯,釋放對該服務的鎖定,等待隊列中的下我的員又開始鎖定服務辦理業務了。
三、ConcurrencyMode.Multiple
多線程模式,多線程模式能夠很好地增長系統的吞吐量。當多個用戶請求服務實例時,服務並不會加鎖,而是同時爲多個請求服務。這樣一來對全部用戶共享的資源就會產生影響,因此這種多線程的訪問模式須要對共享資源作好保護,大部份的狀況下需咱們的手動編寫代碼來實現多線程之間的訪問保護。
例如:咱們去吃烤肉串,烤肉串的師傅能夠同時爲多個顧客烤制,而不用一個一個地排隊等待,這就是典型的多線程處理模式。但這種模式若是不對肉串很好保護得的話那可麻煩了,比仿,我要的是3串麻辣味的,你要的是3串香辣味的,他要的是3串原味的,烤肉的時傅在烤制的時候須要對這三份肉串作好保護,防止作出一些「五味俱全」的肉串來。
使用相同的代理,一個單獨的客戶端能夠像服務端發起多個併發的調用請求。客戶端能夠多線程去激活調用,也能夠在同一個線程上發起快速、連續的單向調用。同一客戶端的請求是否被服務併發處理徹底取決與服務的實例模式、服務配置的併發模式以及傳輸模式(傳輸會話)。
單調服務
對於單調服務,若是沒有傳輸層會話,那麼併發處理是容許的。調用請求在到達服務端後會分發給每一個新的服務實例,而後併發執行。這是一種能夠無論服務併發模式的狀況。
若是單調服務使用傳輸層會話,那麼是否併發處理客戶端請求就取決於服務配置的併發模式。若是服務配置爲ConcurrentcyMode.Single,就不容許使用併發處理,每次只會分發一個調用消息。緣由是使用了ConcurrentcyMode.Single,WCF會維護傳輸層會話,這樣消息會嚴格按照發送的順序執行。應該避免使用太久的處理,由於可能會致使超時。
若是服務配置爲ConcurrentcyMode.Multiple,那麼是容許併發的。調用會在到達時當即分發給新的服務實例進行併發處理。它的好處就是吞吐量,使用ConcurrentcyMode.Multiple搭配單調服務是個好主意——實例自己是線程安全的,因此不須要額外使用併發機制。
若是服務配置了ConcurrencyMode.Reetrant且服務沒有調出,則結果就與ConcurrentcyMode.Single同樣。
會話與單例服務
在使用會話和單例服務時,併發模式會控制等待調用的併發執行工做。若是服務配置了ConcurrentcyMode.Single併發模式,那麼每次會發送給服務實例一個調用消息,等待的調用會放進一個隊列中。應該避免使用太久的處理,由於可能會致使超時。
若是服務併發模式配置爲ConcurrencyMode.Multiple,那麼容許同時處理來自同一客戶端的併發調用。調用消息在到達服務端時就會當即處理(取決於限流設置)。固然,還要處理服務實例的同步訪問問題,不然可能帶來一些風險。
若是服務併發模式配置爲ConcurrencyMode.Reetant,那麼服務實例的行爲就與ConcurrentcyMode.Single相似。可是,若是服務調出,下一個調用就能夠繼續執行。