当前位置: 首页 > news >正文

多线程六脉神剑第四剑:读写锁 (ReaderWriterLockSlim)

文章目录

  • 1、举个栗子
  • 2、读写锁的核心本质
    • 2.1 状态管理机制
    • 2.2 三种锁模式
  • 3、底层原理深入解析
    • 3.1 锁的兼容性矩阵
    • 3.2 原子状态管理
    • 3.3 公平性策略
  • 4、读写锁的完整使用
    • 4.1 基础用法 - 缓存系统
  • 4.2 高级用法 - 可升级读锁
    • 4.3 复杂协调 - 数据聚合器
    • 4.4 超时和尝试获取
  • 5、性能优势和适用场景
    • 5.1 性能对比测试
  • 6、最佳实践和注意事项
    • 6.1 正确模式
    • 6.2 常见陷阱
  • 7、总结

读写锁(ReaderWriterLockSlim)这是处理"读多写少"场景的利器。

1、举个栗子

场景:图书馆的阅览室

  • 读者:可以同时进入多人阅读(多个线程并发读取)

  • 写者(图书管理员):必须独占整个阅览室来整理书籍(一个线程独占写入)

  • 规则

    • 当有读者在阅读时,其他读者可以继续进入

    • 当有写者在工作时,所有读者和其他写者都必须等待

    • 写者工作时,新的读者也不能进入

读写锁的本质:区分读操作和写操作,允许读操作并发执行,但写操作必须独占。

2、读写锁的核心本质

2.1 状态管理机制

读写锁内部维护多个状态计数器:

// 概念性结构 - 展示读写锁的内部状态
class ReaderWriterLockSlimState
{int ReadCount;           // 当前活跃的读者数量int WriteCount;          // 写者数量(0或1)int UpgradeableReadCount; // 可升级读锁数量(0或1)int WaitingReadCount;    // 等待读取的线程数int WaitingWriteCount;   // 等待写入的线程数Thread WritingThread;    // 当前持有写锁的线程Thread UpgradeableThread; // 当前持有可升级读锁的线程bool WriteLockHeld;      // 写锁是否被持有bool UpgradeableLockHeld; // 可升级读锁是否被持有
}

2.2 三种锁模式

class LockModes
{// 1. 读锁(Read Lock)- 共享锁//    多个线程可以同时持有void EnterReadLock();void ExitReadLock();// 2. 写锁(Write Lock)- 独占锁  //    同一时间只有一个线程可以持有void EnterWriteLock();void ExitWriteLock();// 3. 可升级读锁(Upgradeable Read Lock)//    开始是读锁,后面可以升级为写锁void EnterUpgradeableReadLock();void ExitUpgradeableReadLock();
}

3、底层原理深入解析

3.1 锁的兼容性矩阵

读写锁的核心是基于锁的兼容性规则:

当前锁状态读锁请求写锁请求可升级读锁请求
无锁✅ 允许✅ 允许✅ 允许
有读锁✅ 允许❌ 拒绝✅ 允许
有写锁❌ 拒绝❌ 拒绝❌ 拒绝
有可升级读锁✅ 允许❌ 拒绝❌ 拒绝

3.2 原子状态管理

class ReaderWriterLockSlimInternal
{private int _state; // 使用一个32位整数打包多个状态// 状态位的分解(概念性)const int READ_COUNT_BITS = 16;const int WRITE_COUNT_BITS = 8;const int UPGRADE_COUNT_BITS = 8;public bool TryEnterReadLock(){int oldState, newState;do{oldState = _state;// 检查是否可以获取读锁:// 1. 没有写锁被持有// 2. 没有写锁在等待(考虑公平性)// 3. 没有可升级读锁在等待升级if (HasWriteLock(oldState) || (HasWaitingWriters(oldState) && !HasReaders(oldState))){return false; // 不能获取读锁}// 增加读者计数newState = oldState + (1 << READ_COUNT_SHIFT);} while (Interlocked.CompareExchange(ref _state, newState, oldState) != oldState);return true;}public bool TryEnterWriteLock(){int oldState, newState;do{oldState = _state;// 检查是否可以获取写锁:// 1. 没有活跃的读者// 2. 没有写锁被持有// 3. 没有可升级读锁被其他线程持有if (HasReaders(oldState) || HasWriteLock(oldState) || HasUpgradeableLock(oldState)){return false;}// 设置写锁标志newState = oldState | WRITE_LOCK_FLAG;} while (Interlocked.CompareExchange(ref _state, newState, oldState) != oldState);return true;}
}

3.3 公平性策略

为了避免写者饥饿,读写锁采用公平策略:

class FairnessPolicy
{// 当有写者在等待时:public bool ShouldBlockNewReader(){// 如果有写者在等待,并且当前没有活跃读者// 那么新的读者应该等待,让写者先执行return (_waitingWriteCount > 0) && (_activeReadCount == 0);}// 当有读者在等待时:public bool ShouldBlockNewWriter(){// 写者总是要等待所有活跃读者完成return _activeReadCount > 0;}
}

4、读写锁的完整使用

4.1 基础用法 - 缓存系统

class ThreadSafeCache<TKey, TValue>
{private readonly ReaderWriterLockSlim _cacheLock = new ReaderWriterLockSlim();private readonly Dictionary<TKey, TValue> _cache = new Dictionary<TKey, TValue>();private readonly TimeSpan _timeout = TimeSpan.FromSeconds(5);// 读取操作 - 使用读锁(允许多个线程并发读取)public bool TryGetValue(TKey key, out TValue value){_cacheLock.EnterReadLock();try{return _cache.TryGetValue(key, out value);}finally{_cacheLock.ExitReadLock();}}// 写入操作 - 使用写锁(独占访问)public void AddOrUpdate(TKey key, TValue value){if (!_cacheLock.TryEnterWriteLock(_timeout)){throw new TimeoutException("获取写锁超时");}try{_cache[key] = value;Console.WriteLine($"缓存已更新: {key} = {value} (线程: {Thread.CurrentThread.ManagedThreadId})");}finally{_cacheLock.ExitWriteLock();}}// 批量读取 - 多个线程可以同时执行public Dictionary<TKey, TValue> GetAllValues(){_cacheLock.EnterReadLock();try{// 模拟耗时读取操作Thread.Sleep(100);return new Dictionary<TKey, TValue>(_cache);}finally{_cacheLock.ExitReadLock();}}public int Count{get{_cacheLock.EnterReadLock();try{return _cache.Count;}finally{_cacheLock.ExitReadLock();}}}
}// 使用示例
class CacheExample
{static void Main(){var cache = new ThreadSafeCache<string, string>();// 启动多个读取线程var readers = new List<Thread>();for (int i = 0; i < 5; i++){int readerId = i;readers.Add(new Thread(() => ReaderWork(cache, readerId)));}// 启动写入线程var writer = new Thread(() => WriterWork(cache));// 启动所有线程readers.ForEach(t => t.Start());writer.Start();// 等待完成readers.ForEach(t => t.Join());writer.Join();Console.WriteLine($"最终缓存项数: {cache.Count}");}static void ReaderWork(ThreadSafeCache<string, string> cache, int readerId){for (int i = 0; i < 10; i++){string key = $"key_{i % 3}";if (cache.TryGetValue(key, out string value)){Console.WriteLine($"读者{readerId} 读取: {key} = {value}");}Thread.Sleep(50);}}static void WriterWork(ThreadSafeCache<string, string> cache){for (int i = 0; i < 5; i++){cache.AddOrUpdate($"key_{i}", $"value_{DateTime.Now:HH:mm:ss.fff}");Thread.Sleep(200);}}
}

4.2 高级用法 - 可升级读锁

这是读写锁最强大的特性,用于"读后写"场景:

class UpgradeableCache<TKey, TValue> where TValue : new()
{private readonly ReaderWriterLockSlim _cacheLock = new ReaderWriterLockSlim();private readonly Dictionary<TKey, TValue> _cache = new Dictionary<TKey, TValue>();// 获取或添加模式 - 使用可升级读锁public TValue GetOrAdd(TKey key, Func<TKey, TValue> valueFactory){// 第一步:获取可升级读锁(允许多个读者,但只能有一个可升级读者)_cacheLock.EnterUpgradeableReadLock();try{// 第二步:检查缓存中是否已存在TValue value;if (_cache.TryGetValue(key, out value)){Console.WriteLine($"缓存命中: {key} (线程: {Thread.CurrentThread.ManagedThreadId})");return value;}// 第三步:不存在,升级为写锁_cacheLock.EnterWriteLock();try{// 双重检查,防止其他线程已经添加if (!_cache.TryGetValue(key, out value)){Console.WriteLine($"缓存未命中,创建: {key} (线程: {Thread.CurrentThread.ManagedThreadId})");value = valueFactory(key);_cache[key] = value;}return value;}finally{_cacheLock.ExitWriteLock();}}finally{_cacheLock.ExitUpgradeableReadLock();}}// 懒惰初始化模式public TValue GetOrCreate(TKey key) where TValue : new(){return GetOrAdd(key, k => new TValue());}
}// 使用示例
class UpgradeableExample
{static void Main(){var cache = new UpgradeableCache<string, List<int>>();Parallel.For(0, 20, i => {var list = cache.GetOrCreate("shared_list");lock (list){list.Add(i);Console.WriteLine($"线程 {Thread.CurrentThread.ManagedThreadId} 添加 {i}, 列表大小: {list.Count}");}});var finalList = cache.GetOrCreate("shared_list");Console.WriteLine($"最终列表大小: {finalList.Count}");}
}

4.3 复杂协调 - 数据聚合器

class DataAggregator
{private readonly ReaderWriterLockSlim _rwLock = new ReaderWriterLockSlim();private readonly Dictionary<string, double> _metrics = new Dictionary<string, double>();private readonly List<string> _log = new List<string>();private DateTime _lastResetTime = DateTime.Now;// 高频更新 - 使用写锁public void UpdateMetric(string name, double value){_rwLock.EnterWriteLock();try{_metrics[name] = value;_log.Add($"{DateTime.Now:HH:mm:ss.fff} 更新 {name} = {value}");// 限制日志大小if (_log.Count > 1000){_log.RemoveRange(0, _log.Count - 100);}}finally{_rwLock.ExitWriteLock();}}// 低频读取 - 使用读锁(多个监控线程可以并发读取)public MetricsSnapshot GetSnapshot(){_rwLock.EnterReadLock();try{return new MetricsSnapshot{Metrics = new Dictionary<string, double>(_metrics),LastResetTime = _lastResetTime,Timestamp = DateTime.Now};}finally{_rwLock.ExitReadLock();}}// 报告生成 - 使用可升级读锁public Report GenerateReport(){_rwLock.EnterUpgradeableReadLock();try{var snapshot = new MetricsSnapshot{Metrics = new Dictionary<string, double>(_metrics),LastResetTime = _lastResetTime,Timestamp = DateTime.Now};// 如果报告需要重置数据,升级为写锁if (snapshot.Timestamp - _lastResetTime > TimeSpan.FromMinutes(5)){_rwLock.EnterWriteLock();try{// 重置过期数据_lastResetTime = DateTime.Now;_metrics.Clear();_log.Clear();Console.WriteLine("数据已重置");}finally{_rwLock.ExitWriteLock();}}return new Report { Snapshot = snapshot, LogEntries = _log.Take(10).ToList() };}finally{_rwLock.ExitUpgradeableReadLock();}}public class MetricsSnapshot{public Dictionary<string, double> Metrics { get; set; }public DateTime LastResetTime { get; set; }public DateTime Timestamp { get; set; }}public class Report{public MetricsSnapshot Snapshot { get; set; }public List<string> LogEntries { get; set; }}
}

4.4 超时和尝试获取

class TimeoutExample
{private readonly ReaderWriterLockSlim _rwLock = new ReaderWriterLockSlim();private readonly Dictionary<string, string> _data = new Dictionary<string, string>();public bool TryReadWithTimeout(string key, out string value, int timeoutMs = 5000){if (_rwLock.TryEnterReadLock(timeoutMs)){try{return _data.TryGetValue(key, out value);}finally{_rwLock.ExitReadLock();}}else{value = null;Console.WriteLine($"获取读锁超时 ({timeoutMs}ms)");return false;}}public bool TryWriteWithTimeout(string key, string value, int timeoutMs = 5000){if (_rwLock.TryEnterWriteLock(timeoutMs)){try{_data[key] = value;return true;}finally{_rwLock.ExitWriteLock();}}else{Console.WriteLine($"获取写锁超时 ({timeoutMs}ms)");return false;}}public bool TryUpgradeableReadWithTimeout(string key, Func<string, string> factory, int timeoutMs = 5000){if (_rwLock.TryEnterUpgradeableReadLock(timeoutMs)){try{string existingValue;if (_data.TryGetValue(key, out existingValue)){return true;}// 尝试升级if (_rwLock.TryEnterWriteLock(timeoutMs)){try{// 双重检查if (!_data.ContainsKey(key)){_data[key] = factory(key);}return true;}finally{_rwLock.ExitWriteLock();}}else{Console.WriteLine($"升级写锁超时 ({timeoutMs}ms)");return false;}}finally{_rwLock.ExitUpgradeableReadLock();}}else{Console.WriteLine($"获取可升级读锁超时 ({timeoutMs}ms)");return false;}}
}

5、性能优势和适用场景

5.1 性能对比测试

class PerformanceBenchmark
{private readonly ReaderWriterLockSlim _rwLock = new ReaderWriterLockSlim();private readonly object _monitorLock = new object();private int _sharedData = 0;public void TestReaderWriterLock(int readerCount, int writerCount){var stopwatch = Stopwatch.StartNew();var readers = new List<Thread>();var writers = new List<Thread>();// 创建读者线程for (int i = 0; i < readerCount; i++){readers.Add(new Thread(() => {for (int j = 0; j < 1000; j++){_rwLock.EnterReadLock();try{int value = _sharedData; // 读取Thread.SpinWait(100); // 模拟读取工作}finally{_rwLock.ExitReadLock();}}}));}// 创建写者线程for (int i = 0; i < writerCount; i++){writers.Add(new Thread(() => {for (int j = 0; j < 100; j++){_rwLock.EnterWriteLock();try{_sharedData++; // 写入Thread.SpinWait(500); // 模拟写入工作}finally{_rwLock.ExitWriteLock();}}}));}// 启动所有线程readers.ForEach(t => t.Start());writers.ForEach(t => t.Start());// 等待完成readers.ForEach(t => t.Join());writers.ForEach(t => t.Join());Console.WriteLine($"读写锁: {readerCount}读者/{writerCount}写者 -> {stopwatch.ElapsedMilliseconds}ms");}public void TestMonitorLock(int readerCount, int writerCount){var stopwatch = Stopwatch.StartNew();var readers = new List<Thread>();var writers = new List<Thread>();// 创建读者线程for (int i = 0; i < readerCount; i++){readers.Add(new Thread(() => {for (int j = 0; j < 1000; j++){lock (_monitorLock){int value = _sharedData; // 读取Thread.SpinWait(100); // 模拟读取工作}}}));}// 创建写者线程for (int i = 0; i < writerCount; i++){writers.Add(new Thread(() => {for (int j = 0; j < 100; j++){lock (_monitorLock){_sharedData++; // 写入Thread.SpinWait(500); // 模拟写入工作}}}));}// 启动所有线程readers.ForEach(t => t.Start());writers.ForEach(t => t.Start());// 等待完成readers.ForEach(t => t.Join());writers.ForEach(t => t.Join());Console.WriteLine($"Monitor锁: {readerCount}读者/{writerCount}写者 -> {stopwatch.ElapsedMilliseconds}ms");}
}

6、最佳实践和注意事项

6.1 正确模式

class BestPractices
{private readonly ReaderWriterLockSlim _rwLock = new ReaderWriterLockSlim();public void GoodPattern1(){// 总是使用 try-finally_rwLock.EnterReadLock();try{// 读取操作}finally{_rwLock.ExitReadLock();}}public void GoodPattern2(){// 可升级读锁的正确用法_rwLock.EnterUpgradeableReadLock();try{// 检查条件if (NeedWriteOperation()){_rwLock.EnterWriteLock();try{// 写入操作}finally{_rwLock.ExitWriteLock();}}}finally{_rwLock.ExitUpgradeableReadLock();}}public void GoodPattern3(){// 使用超时避免死锁if (_rwLock.TryEnterWriteLock(5000)){try{// 写入操作}finally{_rwLock.ExitWriteLock();}}else{// 超时处理}}
}

6.2 常见陷阱

class CommonMistakes
{private ReaderWriterLockSlim _rwLock = new ReaderWriterLockSlim();// 错误1:忘记释放锁public void Mistake1(){_rwLock.EnterReadLock();// 如果这里发生异常,锁永远不会释放!// 应该使用 try-finally_rwLock.ExitReadLock();}// 错误2:在可升级读锁外尝试升级public void Mistake2(){_rwLock.EnterReadLock();try{// 不能直接从读锁升级!_rwLock.EnterWriteLock(); // 抛出 LockRecursionException}finally{_rwLock.ExitReadLock();}}// 错误3:递归死锁public void Mistake3(){_rwLock.EnterReadLock();try{// 同一个线程内,读锁中不能再获取读锁AnotherReadMethod(); // 如果里面也获取读锁,会死锁}finally{_rwLock.ExitReadLock();}}// 错误4:错误的锁顺序public void Mistake4(){Thread t1 = new Thread(() => {_rwLock.EnterReadLock();Thread.Sleep(1000);_rwLock.EnterWriteLock(); // 死锁!});Thread t2 = new Thread(() => {Thread.Sleep(100);_rwLock.EnterWriteLock(); // 被t1的读锁阻塞});}
}

7、总结

读写锁的本质:

  • 区分读写操作的智能锁

  • 通过状态机管理锁的兼容性

  • 使用原子操作维护读者计数和写者状态

  • 采用公平策略避免写者饥饿

核心优势:

  1. 读操作并发:多个线程可以同时读取,极大提升读密集型应用性能

  2. 写操作安全:保证写操作的原子性和一致性

  3. 灵活的升级:支持从读锁安全升级到写锁

  4. 避免锁竞争:读操作之间不会相互阻塞

适用场景:

  • 读多写少的数据结构(缓存、配置、元数据)

  • 高频读取、低频更新的业务场景

  • 需要生成快照的监控和统计系统

  • 延迟初始化模式

使用要点:

  • 读多写少的场景使用性能最佳

  • 总是使用 try-finally 确保锁释放

  • 合理使用可升级读锁避免死锁

  • 考虑使用超时控制避免长时间阻塞

http://www.dtcms.com/a/525749.html

相关文章:

  • 网站设关键字wordpress搭建问答系统
  • 泉州高端网站建设微信h5免费制作网站模板下载
  • 第13章-人员管理
  • Maya Python入门:属性连接connectAttr()、创建节点createNode()
  • Java学习之旅第三季-17:Lambda表达式
  • 企业电子商务网站建设和一般建设网站收费标准
  • 【深度学习】深度学习核心:优化与正则化超详细笔记
  • 南昌做网站哪个好如何做好网站推广工作
  • 网站网速慢网站正在建设中_敬请期待
  • 影刀:自动化测试网页应用
  • 做彩票网站要什么接口只放一个图片做网站
  • git重写历史
  • 免费下载app软件网站寻找网站建设公司
  • 动易手机网站外贸商城源码
  • 简述网站建设流程中的各个步骤wordpress破解主题商务
  • 2025年--Lc213-2000. 反转单词前缀-Java版
  • safari针对带有loading=lazy属性img的无奈
  • 需求上线部署流程
  • wap网站生成微信小程序网页微信版官网登录仅传输文件
  • php 数据录入网站网站设计制作公司
  • 网络与信息安全基础
  • 权重的网站网站建设一般多少钱网址
  • Why is it called “callback function“
  • axios响应发生错误时的情况列表
  • 网站备案 接口360网页游戏大厅官网
  • vue导出数据到excel
  • 网站续费一年多少钱西安旅游网站建设
  • 探码科技再获“专精特新”认定:Baklib引领AI内容管理新方向
  • 郑州大型网站建设价格百度广告联盟app
  • 简述网站开发具体流程巫山集团网站建设