std::lock能安全同时锁定多个互斥量并避免死锁,需配合std::defer_lock构造的std::unique_lock使用,不支持裸mutex或std::lock_guard,且异常安全自动回滚。

std::lock 能同时锁多个 std::mutex,且自动避免死锁
它不是简单地按顺序调用 lock(),而是用“升级锁”策略(如试探性加锁 + 退避重试)确保所有互斥量最终都能成功获取,不会因加锁顺序不一致导致死锁。这是 c++11 引入的防死锁标准方案,比手写 try_lock() 循环靠谱得多。
必须配合 std::defer_lock 使用,否则编译失败
std::lock 要求传入的互斥量处于“未持有锁”状态;如果直接传入已 lock() 的 std::mutex,行为未定义。所以得先用 std::defer_lock 构造 std::unique_lock,再把它们交给 std::lock:
std::mutex m1, m2; std::unique_lock lk1(m1, std::defer_lock); std::unique_lock lk2(m2, std::defer_lock); std::lock(lk1, lk2); // ✅ 安全加锁
- 不能写
std::lock(m1, m2)——std::lock只接受可锁定对象(如std::unique_lock),不接受裸std::mutex - 也不能先
m1.lock(); std::lock(lk1, lk2)——lk1此时已持锁,std::lock会抛std::system_error(错误码为resource_deadlock_would_occur)
std::lock_guard 不支持 std::lock,必须用 std::unique_lock
std::lock_guard 构造即加锁、析构即释放,不可手动控制加锁时机,因此无法与 std::lock 配合。唯一可行路径是:
- 全部改用
std::unique_lock+std::defer_lock - 调用
std::lock(lk1, lk2, ...) - 后续可安全使用
lk1.owns_lock()判断、lk1.unlock()手动释放(但通常不需要)
注意:一旦 std::lock 成功返回,所有 std::unique_lock 就都已持锁,且保证无死锁 —— 这是它和手写逻辑最本质的区别。
立即学习“C++免费学习笔记(深入)”;
异常安全:std::lock 失败时自动回滚已锁的互斥量
如果在尝试锁多个互斥量过程中某一步失败(例如被信号中断或系统资源不足),std::lock 会自动对已成功加锁的那些调用 unlock(),确保不留半把锁。这意味着你无需 try-catch 包裹 std::lock 来做清理 —— 它自己搞定。
- 但要注意:
std::lock本身可能抛std::system_error(如operation_not_permitted),虽然极少发生 - 真正要小心的是锁住之后、业务逻辑里抛异常 —— 这时候依赖
std::unique_lock的 RAII 自动析构才关键
多互斥量场景下,漏掉 std::defer_lock 或误用 std::lock_guard 是最常踩的坑,这两处一错,死锁或编译失败就立刻出现。