WCF 並發機制的作用對象是 InstanceContext(封裝服務實例的上下文)。不同的實例上下文模式(InstanceContextMode)決定了 InstanceContext 的生命週期與共享範圍,從而根本性地影響並發行為。本文結合監控程式實證,深度剖析三種模式下的並發表現差異。
| 模式 | InstanceContext 生命週期 | 共享範圍 |
|---|---|---|
PerCall | 每次調用創建新實例,調用結束釋放 | 完全隔離,無共享 |
PerSession | 每個會話(客戶端代理)創建一個,會話結束釋放 | 同一會話共享 |
Single | 服務啟動創建單一實例,服務停止釋放 | 所有調用共享 |
PerCall 下每個請求獲得獨立 InstanceContext,**不存在對同一 InstanceContext 的並發調用**。預設 `ConcurrencyMode.Single` 的鎖機制作用於不同 InstanceContext,互不干擾,表現為並行處理。
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
public class CalculatorService : ICalculator { ... }
// 客戶端:5 個執行緒,各建立獨立 Channel
for (int i=1; i<=5; i++) ThreadPool.QueueUserWorkItem(_ => {
var proxy = factory.CreateChannel();
using (proxy as IDisposable) { proxy.Add(1,2); }
});

結果:5 個請求並行完成,總耗時 ≈ 單次調用耗時。
// 客戶端:共用同一 Channel 發起 5 個並發調用
var proxy = factory.CreateChannel();
for (int i=1; i<=5; i++) ThreadPool.QueueUserWorkItem(_ => {
using (new OperationContextScope(proxy)) { proxy.Add(1,2); }
});

**驚人發現**:同一 Channel 並發調用表現為**串行**!原因非 WCF 並發鎖,而是 **Channel 自動開啟機制**:未顯式 `Open()` 的 Channel,WCF 客戶端運行時會將調用排隊,等待前一調用完成後才發送下一個。
var proxy = factory.CreateChannel();
(proxy as ICommunicationObject).Open(); // 關鍵!
for (int i=1; i<=5; i++) ThreadPool.QueueUserWorkItem(_ => { ... });
顯式 `Open()` 後,並發調用恢復並行處理。
即使顯式 `Open()`,若使用 **支援會話的綁定**(`NetTcpBinding`、`NetNamedPipeBinding`、`WS2007HttpBinding`+Security/ReliableSession),同一 Channel 並發調用**仍會串行**!這是 WCF 通道架構設計決策:會話綁定強制同一 Session 內調用有序處理。
設定 `ReleaseServiceInstanceOnTransactionComplete = true` 時,**必須**搭配 `ConcurrencyMode.Single`,否則寄宿拋出 `InvalidOperationException`。
// 錯誤
[ServiceBehavior(ReleaseServiceInstanceOnTransactionComplete = true, ConcurrencyMode = ConcurrencyMode.Multiple)]
// 正確
[ServiceBehavior(ReleaseServiceInstanceOnTransactionComplete = true, ConcurrencyMode = ConcurrencyMode.Single)]

原因:事務完成釋放實例時,Multiple 模式下可能有其他執行緒正在使用該實例,導致競態條件。
| 場景 | InstanceContextMode | ConcurrencyMode | 關鍵考量 |
|---|---|---|---|
| 無狀態、高吞吐、無會話需求 | PerCall | Single (預設) | 最簡單,天然執行緒安全 |
| 有狀態、會話相關、低並發 | PerSession | Single | 會話內串行,保護實例狀態 |
| 有狀態、會話相關、高並發、自行鎖 | PerSession | Multiple | 需確保實例成員執行緒安全 |
| 雙工回調、需回調時處理新請求 | PerSession/Single | Reentrant | 回調時釋放鎖允許重入 |
| 全域單例狀態、高並發 | Single | Multiple | 極高難度,建議改用無狀態設計 |
| 事務完整性要求釋放實例 | 任意 | Single (強制) | 搭配 ReleaseServiceInstanceOnTransactionComplete |
WCF 如何對 InstanceContext 實現同步鎖?`ConcurrencyMode.Single` 下的請求隊列如何管理?`Reentrant` 如何檢測回調並暫時釋放鎖?請關注後續文章剖析 WCF 並發同步核心源碼邏輯。
理解 InstanceContextMode 與 ConcurrencyMode 的交互語義,是構建高效、正確 WCF 服務的關鍵基石。