readerwriterlockslim 更适合高并发,因其轻量无全局锁、支持递归/超时/取消,吞吐量达旧版3–5倍;需成对调用enter/exit方法并用try/finally保护,避免锁泄漏;升级读锁有竞态风险,多数场景应直接用写锁;必须且仅调用一次dispose()防句柄泄漏。

ReaderWriterLockSlim 为什么比 ReaderWriterLock 更适合高并发
因为 ReaderWriterLockSlim 是轻量级实现,没有全局锁开销,支持递归进入、超时等待和取消令牌,而旧的 ReaderWriterLock 内部依赖 Win32 临界区 + 事件对象,争用严重时容易卡死或触发死锁检测异常(ApplicationException)。实际压测中,100+ 线程并发读写下,ReaderWriterLockSlim 的吞吐量通常是前者的 3–5 倍。
注意:它不是线程安全的——构造后必须确保所有访问都通过同一实例,且不能跨 AppDomain 共享。
怎么正确使用 EnterReadLock / EnterWriteLock 和配套释放
必须成对调用 EnterReadLock/ExitReadLock 或 EnterWriteLock/ExitWriteLock,否则会泄漏锁计数,最终导致其他线程永久阻塞。推荐用 try/finally 包裹,而不是 using(它不实现 IDisposable)。
- 写操作必须用
EnterWriteLock—— 即使只读取内部状态做判断,只要后续有写入,就得先拿写锁 - 读操作可并发,但若读取结果用于后续写决策(比如“如果值为 null 就 new 一个”),必须升级为写锁,否则存在竞态
-
TryEnterReadLock(int)和TryEnterWriteLock(int)支持毫秒级超时,避免无限等待;返回false表示未获取到锁,需自行处理退避或失败逻辑
升级读锁到写锁的陷阱:UpgradeToWriteLock 不是万能解
UpgradeToWriteLock 看似方便,但它会先释放读锁、再尝试获取写锁——中间存在极短时间窗口,其他写线程可能插入并修改数据,导致你基于旧读结果做的判断失效。真实场景中,90% 的“升级需求”其实该直接用写锁起步。
仅当满足以下全部条件时才考虑升级:
- 读操作耗时明显长于写操作(如加载大对象)
- 写操作极少发生(比如配置热更新)
- 业务允许“读到旧值后写失败也接受”,即写入前需再次校验关键条件(例如
if (cache == null) cache = Load();必须在写锁内重判)
否则,直接 lock.EnterWriteLock() 开始更清晰、更安全。
Dispose() 必须调用,且只能调用一次
ReaderWriterLockSlim 持有非托管资源(如内核同步对象句柄),不调用 Dispose() 会导致句柄泄漏,进程运行越久越容易触发 OutOfMemoryException 或系统级句柄耗尽。它不支持重复 Dispose(),第二次调用会抛 ObjectDisposedException。
典型错误模式:
var rwLock = new ReaderWriterLockSlim(); // ... 使用中 rwLock.Dispose(); // 后续又不小心调了一次 rwLock.Dispose(); // ?
建议封装在 IDisposable 类型中统一管理生命周期,或在 static 字段中长期持有时,注册 AppDomain.CurrentDomain.ProcessExit 做兜底清理。











