std::lock_guard是RAII式互斥锁封装,仅构造时lock、析构时unlock;不支持延迟加锁、超时、重试或手动干预,必须严格匹配作用域,多锁需用std::lock+std::adopt_lock避免死锁,不可拷贝、移动或作类成员。

std::lock_guard 就是 RAII 封装互斥锁最直接、最轻量的实现——它不提供任何额外控制,只做两件事:构造时 lock(),析构时 unlock()。用错场景或误用参数,反而比手动加锁更危险。
std::lock_guard 构造即加锁,不支持延迟或手动干预
它没有 try_lock()、unlock() 或超时机制,所有行为由作用域严格绑定。一旦声明,立刻调用 mutex.lock();离开作用域(哪怕因异常退出),必定调用 mutex.unlock()。
- 临界区越短越好,别把它声明在函数开头然后“一路用到底”
- 需要条件等待、重试、或分段加锁?换
std::unique_lock,std::lock_guard不适合 - 想“先检查再决定是否加锁”?不行——构造函数里已经
lock()了,失败就抛std::system_error
多 mutex 加锁必须用 std::lock + std::adopt_lock
直接对两个 std::mutex 各自构造 std::lock_guard,极易引发死锁。正确路径是原子性获取全部锁,再交由 std::lock_guard 管理生命周期。
- 顺序必须一致:
std::lock(mtx1, mtx2)后,std::lock_guard构造时第二个参数必须是std::adopt_lock - 漏写
std::adopt_lock→std::lock_guard会再次调用lock()→ 未定义行为(常见 crash 或阻塞) - 两个线程分别按不同顺序调用
std::lock(mtx_a, mtx_b)和std::lock(mtx_b, mtx_a),仍可能死锁——所以全局约定锁顺序(如地址大小)仍是必要防线
不能复用、不能拷贝、不能跨作用域传递
std::lock_guard 是栈上独占对象,设计上禁止所有权转移。它的存在只为一个目的:让锁的生命周期和作用域完全对齐。
立即学习“C++免费学习笔记(深入)”;
-
std::lock_guard<:mutex> g(mtx);</:mutex>声明后,不能赋值给另一个变量,也不能std::move出去 - 想提前释放锁?用
{ std::lock_guard<:mutex> g(mtx); /* 临界区 */ }</:mutex>显式限定作用域 - 函数返回
std::lock_guard?编译不过——它没移动构造函数,也没拷贝构造函数 - 把它作为类成员?几乎总是错的——成员对象生命周期长于单次临界区,会导致锁持有时间过长,严重拖慢并发
别拿它当递归锁用,标准 std::mutex 不支持重入
同一个线程对同一 std::mutex 多次加锁,无论通过几个 std::lock_guard,结果都是未定义行为。这不是“功能缺失”,而是标准明确禁止。
- 错误写法:
std::lock_guard<:mutex> g1(mtx); std::lock_guard<:mutex> g2(mtx);</:mutex></:mutex>(同一线程) - 如果业务逻辑确实需要同一线程多次进入临界区,必须改用
std::recursive_mutex,并配std::lock_guard<:recursive_mutex></:recursive_mutex> - 注意:递归锁有性能开销,且容易掩盖设计问题——优先考虑重构为无重入需求的逻辑
最容易被忽略的是:它看起来简单,但每个约束都对应一个真实崩溃场景。RAII 的可靠性,全系于你是否严格遵守它的使用边界——不是“能跑就行”,而是“每行代码都在它设计的轨道上”。


















