ReaderWriterLock 已过时,应改用 ReaderWriterLockSlim;前者基于内核对象、开销大、无超时返回机制、不支持锁升级,后者为用户态高性能实现,支持超时、升级/降级、严格配对释放及递归策略控制。

ReaderWriterLock 已被明确标记为过时
ReaderWriterLock 在 .NET Framework 2.0 中引入,但自 .NET Framework 3.5 起就被官方弃用;它未被包含在 .NET Core / .NET 5+ 的默认 API 集中,且在 netstandard2.0 及更高版本中仅作为兼容性类型存在(需引用 System.Threading 程序集)。微软文档和源码注释中均标注其“obsolete”,不建议新项目使用。
- 它基于 Win32 内核同步对象(如
CreateEvent),开销大、响应慢、易死锁 - 没有超时重载的
TryEnterReadLock(int)等方法,超时失败时抛出ApplicationException,而非返回false,难以安全捕获和处理 - 不支持升级锁(upgradable read lock),也无法降级,业务逻辑受限
ReaderWriterLockSlim 是唯一推荐的现代读写锁
ReaderWriterLockSlim 是 .NET Framework 3.5 引入的托管替代方案,专为高性能、低开销、高可控性设计。它完全在用户态实现(无内核切换),支持完整的超时控制、升级/降级语义,并被深度集成进 .NET Core / .NET 5+ 的基础类库中。
- 所有核心方法都有
Try版本:TryEnterReadLock(int)、TryEnterWriteLock(int)、TryEnterUpgradeableReadLock(int),失败直接返回false,避免异常干扰流程 - 支持
EnterUpgradeableReadLock()→EnterWriteLock()升级路径(注意:必须在同一线程内完成,且不能嵌套或跨 await) - 构造函数可传入
LockRecursionPolicy(默认NoRecursion),显式禁止递归加锁,提前暴露线程安全漏洞 - 释放锁必须严格配对:
ExitReadLock()/ExitWriteLock()/ExitUpgradeableReadLock(),否则会引发SynchronizationLockException
常见误用:把 ReaderWriterLockSlim 当成 lock 用
很多人照搬 lock 的写法,只在写操作加锁、读操作裸奔——这在 ReaderWriterLockSlim 下是严重错误,会导致脏读或 KeyNotFoundException(尤其在 Dictionary 上)。
- 所有访问共享资源的代码路径,无论读或写,都必须包裹对应锁:读走
EnterReadLock(),写走EnterWriteLock(),升级写走EnterUpgradeableReadLock()+EnterWriteLock() - 忘记
ExitXxxLock()或在异常路径中遗漏释放,将导致锁永久占用,后续线程无限等待(表现为 CPU 低但请求卡死) - 不要在
async方法中持有ReaderWriterLockSlim锁——它**不是 await 安全的**。若需异步等待,请改用AsyncReaderWriterLock(如 Nito.AsyncEx 提供)
性能与场景选择:不是所有读多写少都该用它
ReaderWriterLockSlim 的优势只在「读远多于写」且「单次读操作耗时明显」时才显著。若读操作极轻(如简单字段访问)、或并发度不高(lock 反而更高效、更简洁。
- 基准测试显示:在高争用(大量写)下,
ReaderWriterLockSlim性能可能低于Monitor(即lock) - 若数据结构本身已线程安全(如
ConcurrentDictionary、ConcurrentQueue),优先用它们,比手写锁更可靠、更免维护 - 真正需要读写分离语义时(例如缓存命中率统计、配置热更新、状态快照读取),再引入
ReaderWriterLockSlim,并确保锁粒度合理(宁拆勿合)
private static readonly Dictionary升级锁、跨线程释放、混用 async/await —— 这些地方不报错,但会在某个高并发时刻突然崩掉,而且很难复现。别等线上报警才查。_cache = new(); private static readonly ReaderWriterLockSlim _rwLock = new(LockRecursionPolicy.NoRecursion); public static bool TryGetValue(string key, out double value) { _rwLock.EnterReadLock(); try { return _cache.TryGetValue(key, out value); } finally { _rwLock.ExitReadLock(); } } public static void SetValue(string key, double value) { _rwLock.EnterWriteLock(); try { _cache[key] = value; } finally { _rwLock.ExitWriteLock(); } }










