多线程访问List<T>如何实现读并发、仅阻塞写的同步机制?
答案
你的需求完全可以实现。你之前用全独占同步块的实现,会让所有读、写线程全部串行执行,在读多写极少的场景下确实会浪费多线程并发性能,你描述的访问控制逻辑是**读写锁(Reader-Writer Lock)**的标准设计目标,和读写锁的特性完全匹配。
匹配需求的读写锁核心规则
读写锁的访问控制逻辑和你提的要求完全一致:
- 多线程读不互斥:只要没有线程持有写锁,任意数量的读线程都可以同时拿到读锁访问列表,读线程之间不会互相阻塞,能充分发挥多线程并发读的性能
- 写操作等待现有读完成:当线程申请写锁执行增删操作时,会进入等待状态,直到所有当前持有读锁的线程全部释放读锁后,才能拿到写锁执行修改
- 写期间阻塞所有新访问:写锁是完全独占的,当有线程持有写锁时,所有新发起的读请求、其他写请求都会被阻塞,直到写锁释放,避免读写并发导致的数据错乱。
落地实现建议
绝大多数主流编程语言的标准库都提供了生产可用的读写锁实现,不要自己从零实现——手写读写锁很容易出现竞态条件、死锁、写线程饥饿(持续有新读线程进入导致写线程永远拿不到锁)等问题。
以你代码里的List<T>(C#/.NET环境)为例,直接使用内置的ReaderWriterLockSlim即可,这个类型就是专门为读多写少场景优化的轻量实现,性能远优于老旧的ReaderWriterLock,基础使用示例如下:
// 共享读写锁实例,注意和共享List声明在同一作用域 private readonly ReaderWriterLockSlim _rwLock = new ReaderWriterLockSlim(); // 你的共享List实例 private readonly List<T> _sharedList = new List<T>(); // 读操作模板 public T GetValueByIndex(int index) { _rwLock.EnterReadLock(); try { // 所有读取List的逻辑全放在try块里 return _sharedList[index]; } finally { // 必须在finally里释放锁,避免逻辑抛异常导致锁永久占用 _rwLock.ExitReadLock(); } } // 写操作(新增/删除/修改)模板 public void AddNewItem(T item) { _rwLock.EnterWriteLock(); try { // 所有修改List的逻辑全放在try块里 _sharedList.Add(item); } finally { _rwLock.ExitWriteLock(); } }
使用注意事项
- 所有对共享List的访问,不管是读还是写,都必须经过对应的锁保护,不能有绕过锁直接访问列表的代码
- 如果存在「先读取判断状态,再执行修改」的复合操作,不要先拿读锁再升级为写锁,直接在入口申请写锁即可,避免判断和写入之间出现竞态
- 不要在持有读锁的时候执行和List读取无关的长耗时操作,否则会导致等待的写线程阻塞时间过长
- 如果你使用的是其他语言,直接选用对应标准库提供的读写锁实现即可,核心使用逻辑和上述示例一致。
内容的提问来源于stack exchange,提问作者M.Toy
相关产品推荐
相关产品推荐

