graph LR
LK(锁) ---> std::lock_guard
LK ---> std::unique_lock
LK ---> std::shared_lock
LK ---> std::scoped_lock
约定
|
|
锁策略
锁策略 | 所有权 | 行为 |
---|---|---|
默认 | 持有 mutex 所有权 | 构造锁时直接调用 .lock() |
std::defer_lock |
不持有 mutex 所有权 | 不调用 .lock() ,手动延后调用 |
std::try_lock |
尝试持有 mutex 所有权,不阻塞 | 构造锁时直接调用 .try_lock() |
std::adopt_lock |
借用 mutex 所有权,假设 mutex 已被占有 | 不调用 .lock() |
lock_guard
最简单的 lock,最纯粹的 raii 风味
|
|
多锁死锁
考虑这种情况
|
|
因为 foo1()
第一时间锁了 _data1_mtx
而 foo2()
第一时间锁了 _data2_mtx
所以当两者几乎同时执行到自己的第二行时,都已经先被对方锁了,造成了死锁
解决方法应使用
lock
或者
scoped_lock
(C++17)
unique_lock
lock_guard 升级版
raii lock/unlock + 手动 lock/unlock + 可移动 + 异常安全
lock
同时锁多个 lock
|
|
SpinLock
Busy-waiting, 无官方实现
通过 while
循环等待避免进入休眠态,避免内核上下文切换の开销
适用于短临界区,真多核环境
shared_lock
ISO 官方读写锁
多线程の读写安全
主流的安全模型 (inc. rust) 认为
- 同时一写无读
- 同时无写多读
是安全的
由多线程の的读写安全可知
方法 | 保护操作 | mutex 所有权 |
---|---|---|
lock_shared() /unlock_shared() |
多读 | 被多个读线程锁持有 |
lock() /unlock() |
一写 | 被一个写线程锁持有 |
scoped_lock
C++17 带来的多锁 quality-of-life 工具
对比一下 std::lock
|
|
vs.
|
|
最佳实践
mm 原则
因为有时候对象的读操作应该对外只要求 const&
但是锁 mutex 操作需要 mutex 非 const
所以建议对所有需要对外提供读操作的 mutex 加上 mutable
标签
|
|