多線程讀寫共享資源時,單純互斥鎖過於保守。讀寫鎖允許:多線程並發讀、單線程獨占寫、讀寫互斥。.NET 提供兩代讀寫鎖,差異巨大。
.NET 1.1 引入,但官方與 Jeffrey Richter 均強烈批評:
Monitor.Enter 慢 5 倍,寫鎖慢 6 倍。UpgradeToWriteLock 先釋放讀鎖再獲寫鎖,窗口期可被其他線程插入修改。.NET 3.5 引入,一次性解決上述問題:
Read(共享)、Write(獨占)、UpgradeableRead(可升級讀,同一時刻僅一線程持有)。UpgradeableRead 時,可決定降級為 Read 或升級為 Write,全程無竞爭窗口、無死鎖風險。rwLock.EnterUpgradeableReadLock();
try
{
if (/* 需更新 */)
{
rwLock.EnterWriteLock();
try { /* 寫入 */ }
finally { rwLock.ExitWriteLock(); }
}
else
{
rwLock.EnterReadLock();
rwLock.ExitUpgradeableReadLock(); // 釋放可升級鎖,允許其他讀者
try { /* 讀取 */ }
finally { rwLock.ExitReadLock(); }
}
}
finally
{
if (isUpdated) rwLock.ExitUpgradeableReadLock();
}
EnterReadLock 拋 LockRecursionException。LockRecursionPolicy.SupportsRecursion,但不建議新代碼使用(增加複雜度與死鎖風險)。IsReadLockHeld、WaitingWriteCount、CurrentReadCount、RecursiveWriteCount 等,便於監控與調試。
內核級輕量讀寫鎖,不支持遞歸、不支持升級/降級,設計目標為無偏向、低開銷。.NET 可通過 P/Invoke 封裝使用:
public sealed class SRWLock
{
private IntPtr rwLock;
public SRWLock() { InitializeSRWLock(out rwLock); }
public void EnterReadLock() { AcquireSRWLockShared(ref rwLock); }
public void EnterWriteLock() { AcquireSRWLockExclusive(ref rwLock); }
public void ExitReadLock() { ReleaseSRWLockShared(ref rwLock); }
public void ExitWriteLock() { ReleaseSRWLockExclusive(ref rwLock); }
// DllImport 略
}
緩存典型場景:大量讀、極少寫。MSDN 經典範例展示 ReaderWriterLockSlim 在字典緩存中的完整用法(含 AddOrUpdate 升級模式)。
極致性能場景可考慮 Lock-Free,但編碼極難、正確性難證。讀寫鎖應用範圍更廣、維護性更好。