C#怎么实现读写锁 ReaderWriterLockSlim使用教程

30次阅读

ReaderWriterLockSlim 是 C# 中适用于“多读少写”场景的高效读写锁,支持超时、取消和递归控制;需成对调用 Enter/ExitReadLock 或 Enter/ExitWriteLock,推荐 try/finally 保障释放,禁用递归与合理超时可提升安全性与性能。

C#怎么实现读写锁 ReaderWriterLockSlim使用教程

ReaderWriterLockSlim 是 C# 中高效、轻量的读写锁实现,适合“多读少写”场景,能显著提升并发性能。它比传统的 lockMonitor 更灵活,允许多个线程同时读,但写操作独占,且支持超时、取消和递归控制(可禁用)。

基本用法:读锁与写锁的正确加解锁

必须成对调用 EnterReadLock/ExitReadLockEnterWriteLock/ExitWriteLock,推荐用 try/finally 保证释放,避免死锁。

  • 读操作:多个线程可同时进入,调用 EnterReadLock() → 访问共享资源 → 必须 ExitReadLock()
  • 写操作:互斥独占,调用 EnterWriteLock() → 修改共享资源 → 必须 ExitWriteLock()
  • 不要混用:不能在持有读锁时直接升级为写锁(会死锁),需先释放读锁再申请写锁

安全写法示例:带超时和异常防护

实际项目中建议启用超时机制,防止无限等待;并始终在 finally 块中释放锁:

private readonly ReaderWriterLockSlim _rwLock = new ReaderWriterLockSlim(); 

public string GetData() { _rwLock.EnterReadLock(); try { return _sharedData; } finally { _rwLock.ExitReadLock(); } }

public void UpdateData(string value) { if (_rwLock.TryEnterWriteLock(1000)) // 等待1秒,超时返回false { try { _sharedData = value; } finally { _rwLock.ExitWriteLock(); } } else { throw new TimeoutException("获取写锁超时"); } }

进阶控制:禁用递归、设置公平模式、响应取消

ReaderWriterLockSlim 构造时可传入参数调整行为:

  • 禁用递归:new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion),避免同一线程重复加锁导致逻辑混乱
  • 启用公平模式:new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion, LockRecursionPolicy.NoRecursion)(注意:.net 6+ 支持 new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion) { UseSpinWait = false } 配合公平队列,但真正公平需手动管理)——更准确地说,它本身不提供严格公平,但可通过 TryEnter* + 重试 + 取消令牌模拟
  • 配合 CancellationToken:使用 TryEnterReadLock(Int32, CancellationToken) 等重载,实现可取消的等待

注意事项与常见坑

几个容易忽略却关键的细节:

  • 务必调用 Dispose() 释放底层内核资源(尤其长期存活对象),建议封装IDisposable 类型或在 using 中创建(但注意:它不是设计为短生命周期对象,通常作为字段长期持有)
  • 不要在锁内调用未知外部方法(如事件、虚方法、linq 查询),可能引发死锁或延长锁持有时间
  • 读锁下禁止修改被保护的数据,否则破坏线程安全;写锁是唯一允许修改的时机
  • 嵌套锁顺序要一致(如先读再写,所有线程都按此顺序),否则易引发死锁

基本上就这些。用好 ReaderWriterLockSlim 的关键是理解“读共享、写独占”的模型,配合适当的超时和异常防护,就能在高并发读场景下兼顾安全与性能。

text=ZqhQzanResources