面對高併發 Web 服務場景:大量唯讀請求、極少寫入請求但寫入耗時極長。傳統 ReaderWriterLock 導致成百上千執行緒阻塞於核心態,寫入釋放瞬間驚群喚醒引發劇烈上下文切換,嚴重拖垮擴展性。本文介紹 **ReaderWriterGate**——一種基於工作佇列、執行緒池、非阻塞回調的創新同步原語,實現「調用者不阻塞、少量執行緒處理大量請求、寫入優先且可提前釋放讀取鎖」的高效能同步模式。
QueueRead/QueueWrite 立即返回,**調用執行緒從不等待**ReaderWriterGateReleaser 主動釋放鎖,續行非關鍵工作public sealed class ReaderWriterGate
{
public ReaderWriterGate();
public void QueueRead(ReaderWriterGateCallback callback, object state);
public void QueueWrite(ReaderWriterGateCallback callback, object state);
// 異步模式支援 ASP.NET 異步頁面
public IAsyncResult BeginRead(ReaderWriterGateCallback callback, object state, AsyncCallback asyncCallback, object asyncState);
public void EndRead(IAsyncResult ar);
public IAsyncResult BeginWrite(ReaderWriterGateCallback callback, object state, AsyncCallback asyncCallback, object asyncState);
public void EndWrite(IAsyncResult ar);
}
public delegate void ReaderWriterGateCallback(ReaderWriterGateReleaser releaser);
public sealed class ReaderWriterGateReleaser : IDisposable
{
public object State { get; } // QueueXxx 第二參數
public ReaderWriterGate Gate { get; } // 所屬 Gate
public void Release(); // 主動釋放
public void Dispose(); // 配合 using 語法
}
internal sealed class CatalogOrderSystem
{
private readonly ReaderWriterGate _gate = new ReaderWriterGate();
// 唯讀:下單查詢型別目錄
public void BuyCatalogProductsWS(CatIDAndQuantity[] items)
{
_gate.QueueRead(BuyCatalogProducts, items);
}
private void BuyCatalogProducts(ReaderWriterGateReleaser r)
{
using (r) // using 區塊結束自動 Dispose → Release
{
var items = (CatIDAndQuantity[])r.State;
foreach (var item in items) { /* 查詢型別、建立訂單 */ }
}
// 鎖已釋放,其他讀/寫可並行
SaveOrderToDatabase(); // 耗時 I/O 不持有鎖
SendConfirmationEmail(); // 耗時 I/O 不持有鎖
}
// 獨佔:更新型別目錄
public void UpdateCatalogWS(CatalogEntry[] entries)
{
// 驗證不持有鎖
_gate.QueueWrite(UpdateCatalog, entries);
}
private void UpdateCatalog(ReaderWriterGateReleaser r)
{
var entries = (CatalogEntry[])r.State;
// 獨佔存取更新型別目錄
// 返回自動釋放
}
}
private readonly ResourceLock _syncLock = new MonitorResourceLock(); // 內部保護鎖
private ReaderWriterGateStates _state = ReaderWriterGateStates.Free;
private int _numReaders = 0;
private readonly Queue<ReaderWriterGateReleaser> _qWrite = new Queue<ReaderWriterGateReleaser>();
private readonly Queue<ReaderWriterGateReleaser> _qRead = new Queue<ReaderWriterGateReleaser>();
enum ReaderWriterGateStates
{
Free, // 空閒
OwnedByReaders, // 唯讀中,無寫等待
OwnedByReadersAndWriterPending, // 唯讀中,有寫等待
OwnedByWriter // 寫入中
}
// 簡化邏輯
lock (_syncLock)
{
switch (_state)
{
case Free:
_state = OwnedByWriter;
ThreadPool.QueueUserWorkItem(ExecuteCallback, releaser); // 執行寫回調
break;
case OwnedByReaders:
case OwnedByReadersAndWriterPending:
_state = OwnedByReadersAndWriterPending;
_qWrite.Enqueue(releaser);
break;
case OwnedByWriter:
_qWrite.Enqueue(releaser);
break;
}
}
lock (_syncLock)
{
if (_state == Free || _state == OwnedByReaders)
{
_state = OwnedByReaders;
_numReaders++;
ThreadPool.QueueUserWorkItem(ExecuteCallback, releaser); // 執行讀回調
}
else // OwnedByWriter 或 OwnedByReadersAndWriterPending
{
_qRead.Enqueue(releaser);
}
}
// 執行緒池執行回調結束時呼叫
private void OnCallbackComplete(ReaderWriterGateReleaser releaser, bool isWriter)
{
lock (_syncLock)
{
if (!isWriter) // 讀回調結束
{
_numReaders--;
if (_numReaders > 0) return; // 還有其他讀者,不調度新工作
}
// 無讀者或寫回調結束,檢查佇列
if (_qWrite.Count > 0) // 寫優先
{
_state = OwnedByWriter;
var next = _qWrite.Dequeue();
ThreadPool.QueueUserWorkItem(ExecuteCallback, next);
}
else if (_qRead.Count > 0)
{
_state = OwnedByReaders;
_numReaders = _qRead.Count;
// **一次性喚醒所有讀者** — 但由執行緒池控制並行度!
while (_qRead.Count > 0)
{
var next = _qRead.Dequeue();
ThreadPool.QueueUserWorkItem(ExecuteCallback, next);
}
}
else
{
_state = Free;
}
}
}
Web 服務請求執行緒只做:建立回調物件 → 入佇列/排程 → **立即返回線程池**。無論等待多久,**不佔用請求執行緒、不進入核心等待、不消耗棧空間**。
傳統鎖釋放時喚醒所有等待執行緒 → 作業系統調度數千執行緒。ReaderWriterGate 將所有就緒回調 **一次性排入執行緒池**,執行緒池內部演算法 (山丘演算法) 自動限制同時運行執行緒數 ≈ CPU 核心數。**零上下文切換風暴,極少執行緒完成海量工作**。
內部邏輯永優先處理寫佇列,確保更新操作不被源源不絕的讀請求無限延後。
讀回調中 `using (r) { 唯讀工作 }` 區塊結束即釋放鎖,後續耗時 I/O (存資料庫、發郵件) **不持有鎖**,大幅縮小臨界區,提升並行度。
ASP.NET 2.0 異步頁面需回傳 IAsyncResult。ReaderWriterGate 提供 BeginRead/EndRead、BeginWrite/EndWrite,內部包裝回調完成事件,無縫整合:
public IAsyncResult BeginAsyncOperation(...)
{
return _gate.BeginRead(Callback, state, asyncCallback, asyncState);
}
public void EndAsyncOperation(IAsyncResult ar) => _gate.EndRead(ar);
ReaderWriterGate 內部使用 ResourceLock 抽象 (Power Threading Library),一行代碼切換為 SpinResourceLock:**執行緒完全不進入核心等待,純用戶態自旋**。前提:所有執行緒優先級相同、關閉優先級提升。適合極低延遲、高併發、短臨界區場景。
這種「反直覺」的非阻塞同步設計,重新定義了高併發系統的鎖效能上限,值得每位平台架構師深入研究並應用於關鍵熱路徑。