同一线程中shared_lock与unique_lock共享std::shared_mutex是否合法及死锁原因
这种做法不符合C++标准定义,必然会引发死锁,具体原因和正确处理方式如下:
1. 标准对shared_mutex锁转换的明确限制
C++标准规定,std::shared_mutex不支持同一线程从共享锁(shared_lock)直接升级为独占锁(unique_lock)。当线程持有共享锁时,尝试获取独占锁的操作会无限阻塞——因为独占锁要求所有共享锁(包括当前线程持有的)全部释放,而当前线程正拿着共享锁等待独占锁,形成了自循环的阻塞逻辑。
你代码里用std::adopt_lock属于完全误用:这个参数的含义是「当前线程已经持有该互斥量的独占锁」,但此时你持有的是共享锁,不仅不符合参数要求,还会触发未定义行为,本质上还是没释放共享锁就去抢独占锁,死锁是必然结果。
2. 死锁的具体触发逻辑
你的代码执行流程中:
- 线程先获取
shared_lock,此时mutex处于共享锁定状态,允许其他线程也获取共享锁。 - 接着尝试用
unique_lock获取独占锁,而独占锁的获取条件是「无任何线程持有共享锁或独占锁」。但当前线程自己还握着一个共享锁,这个获取操作会一直阻塞,直到自己释放共享锁——但它又在等独占锁获取成功,直接形成自死锁。
如果此时还有其他线程持有共享锁,情况会更复杂:当前线程等其他线程释放共享锁,其他线程可能也在等待独占锁,最终形成多线程交叉死锁。
3. 正确的处理方式
方式一:先释放共享锁,再获取独占锁
如果不需要原子性的锁升级,可以先完全释放共享锁,再获取独占锁:
std::shared_mutex mutex; void foo() { { std::shared_lock readLock{ mutex }; // ... 只读操作逻辑 ... } // 共享锁在此处释放 { std::unique_lock lock{ mutex }; // ... 读写操作逻辑 ... } { std::shared_lock readLock{ mutex }; // ... 后续只读操作逻辑 ... } }
注意:这种方式会失去共享锁到独占锁之间的原子性——释放共享锁和获取独占锁的间隙,其他线程可能修改数据。
方式二:用原子升级锁(C++17及以上)
如果需要原子性的锁升级,可以使用std::upgrade_lock配合std::shared_mutex的升级接口:
std::shared_mutex mutex; void foo() { std::upgrade_lock upgradeLock{ mutex }; // 先获取可升级的共享锁 // ... 只读操作逻辑 ... { std::unique_lock lock{ std::move(upgradeLock) }; // 原子性升级为独占锁 // ... 读写操作逻辑 ... } // 独占锁释放,回到可升级共享锁状态 // ... 后续只读操作逻辑 ... }
内容的提问来源于stack exchange,提问作者Aleksandr Medvedev
相关产品推荐
相关产品推荐

