c# ReaderWriterLock 和 ReaderWriterLockSlim 的废弃与推荐

来源:这里教程网 时间:2026-02-21 17:37:11 作者:

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<string, double> _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();
    }
}
升级锁、跨线程释放、混用 async/await —— 这些地方不报错,但会在某个高并发时刻突然崩掉,而且很难复现。别等线上报警才查。

相关推荐