為了直觀理解 WCF InstanceContextMode (單例/每會話/每呼叫) 與 ConcurrencyMode (單一/多重/可重入) 對並發行為的影響,本文實作一個雙端監控程式 (WinForms 寄宿服務 + WinForms 客戶端),即時記錄服務呼叫開始/結束、操作執行開始/結束、回調事件等時間戳與客戶端 ID,視覺化呈現並發處理時間軸。
ICalculator (單一 Add(double, double) 操作)、EventType 列舉 (8 種監控事件)、EventMonitor 靜態類別 (事件發布/訂閱、客戶端 ID 透過訊息標頭傳遞)。CalculatorService 標記 [ServiceBehavior(UseSynchronizationContext=false)],Add 方法中 Thread.Sleep(5000) 模擬長時間操作,穿插 EventMonitor.Send(EventType.StartExecute/EndExecute)。ServiceHost 寄宿,ListBox 即時顯示監控日誌,SynchronizationContext.Post 確保 UI 執行緒安全更新。ThreadPool.QueueUserWorkItem 併發建立 5 個 Proxy,各附帶唯一 ClientID 訊息標頭,呼叫 Add(1,2) 並記錄 StartCall/EndCall。ws2007HttpBinding (支援可靠會話與訊息標頭)。public static class EventMonitor
{
public const string ClientIdHeaderNamespace = "https://www.artech.com/";
public const string ClientIdHeaderLocalName = "ClientId";
public static EventHandler MonitoringNotificationSended;
public static void Send(EventType eventType) // 服務端用:從標頭取 ClientID
{
if (MonitoringNotificationSended != null)
{
int clientId = OperationContext.Current.IncomingMessageHeaders.GetHeader(ClientIdHeaderLocalName, ClientIdHeaderNamespace);
MonitoringNotificationSended(null, new MonitorEventArgs(clientId, eventType, DateTime.Now));
}
}
public static void Send(int clientId, EventType eventType) // 客戶端用:直接指定 ClientID
{
if (MonitoringNotificationSended != null)
MonitoringNotificationSended(null, new MonitorEventArgs(clientId, eventType, DateTime.Now));
}
}
for (int i = 1; i <= 5; i++)
{
ThreadPool.QueueUserWorkItem(state =>
{
int clientId = Interlocked.Increment(ref clientIdIndex);
ICalculator proxy = _channelFactory.CreateChannel();
using (proxy as IDisposable)
{
EventMonitor.Send(clientId, EventType.StartCall);
using (OperationContextScope scope = new OperationContextScope(proxy as IContextChannel))
{
var header = new MessageHeader(clientId).GetUntypedHeader(EventMonitor.ClientIdHeaderLocalName, EventMonitor.ClientIdHeaderNamespace);
OperationContext.Current.OutgoingMessageHeaders.Add(header);
proxy.Add(1, 2); // 封鎖 5 秒
}
EventMonitor.Send(clientId, EventType.EndCall);
}
}, null);
}

觀察結果:ConcurrencyMode.Single + InstanceContextMode.Single 導致 5 個併發請求在服務端**序列化執行**,總耗時 ≈ 5秒 × 5 = 25秒。
後續文章將系統驗證以下組合差異:
| InstanceContextMode | ConcurrencyMode | 預期行為 |
|---|---|---|
| PerCall | Single | 每請求新實例,並行執行 (無共享狀態衝突) |
| PerSession | Multiple | 同會話共享實例,多執行緒並行進入操作,需自行同步 |
| Single | Reentrant | 單實例允許重入 (回調時釋放鎖),適合雙工服務 |
完整專案結構 (契約/服務/寄宿/客戶端) 可從文章連結下載,直接編譯執行即可複現實驗。