std::lock_guard适用于简单连续临界区,仅构造/析构自动加锁解锁;std::unique_lock支持延迟锁、条件变量、所有权转移等高级场景,二者不可互换替代。

std::lock_guard 不能替代 std::unique_lock,反过来却可以——但代价是冗余和潜在误用。 两者不是“选哪个更好”,而是“该用哪个就用哪个”。错把 std::unique_lock 当成 std::lock_guard 的升级版来写,反而容易引入空锁、重复解锁或条件变量调用失败等运行时问题。
std::lock_guard 调用 unlock() 就会编译失败
这是最常踩的坑:有人看到 std::unique_lock 有 unlock(),下意识给 std::lock_guard 也加一句,结果报错:
error: 'class std::lock_guard<:mutex>' has no member named 'unlock'</:mutex>
原因很直接:std::lock_guard 根本不提供手动控制接口。它只做两件事:构造时调用 mtx.lock(),析构时调用 mtx.unlock()。
- 适用场景:临界区逻辑连续、无分支、不涉及 I/O 或等待操作
- 实操建议:保护单次
push_back()、++counter这类短操作,配合作用域天然收束 - 别把它声明为类成员——生命周期一拉长,锁就悬着不放
std::unique_lock 支持 defer_lock / try_to_lock 构造策略
std::unique_lock 的灵活性不是靠多几个函数,而是靠构造时的策略选择。比如你得先检查队列非空再加锁:
std::unique_lock<:mutex> ulk(mtx, std::defer_lock);<br>if (!queue.empty()) {<br> ulk.lock();<br> process(queue.front());<br> queue.pop();<br>}</:mutex>
而 std::lock_guard 没有 std::defer_lock 这个选项,一构造就死锁住互斥量,哪怕你后面根本不进临界区。
-
std::try_to_lock对应非阻塞尝试:失败时ulk.owns_lock()返回false,可立刻做 fallback 处理 - 延迟锁能缩短实际持锁时间,对高并发吞吐敏感的场景很关键
- 注意:默认构造的
std::unique_lock是空状态,未绑定任何std::mutex,此时调用lock()会崩溃
std::condition_variable::wait() 只接受 std::unique_lock
这是硬性接口约束,不是风格偏好。下面这段代码无法通过编译:
std::condition_variable cv;<br>std::mutex mtx;<br>std::lock_guard<:mutex> lk(mtx); // ❌ 错误:类型不匹配<br>cv.wait(lk, []{ return ready; });</:mutex>
因为 cv.wait() 内部要临时调用 unlock() 再挂起线程,唤醒后再 lock() —— 这个“解锁-阻塞-重锁”流程要求锁对象支持移动和手动控制。
- 必须写成:
std::unique_lock<:mutex> lk(mtx); cv.wait(lk, []{ return ready; });</:mutex> - 即使你没显式调用
unlock(),wait()也会触发它;析构时仍会补上最终的unlock() - 如果混用
std::lock_guard和条件变量,编译器会直接拒绝,不会等到运行时报错
std::unique_lock 支持 move,std::lock_guard 不行
当你需要把锁传给另一个函数、或者封装在返回值里,std::unique_lock 是唯一选择:
std::unique_lock<:mutex> acquire_lock() {<br> std::unique_lock<:mutex> lk(mtx, std::defer_lock);<br> if (try_acquire()) lk.lock();<br> return std::move(lk); // ✅ 合法<br>}</:mutex></:mutex>
std::lock_guard 禁止拷贝和移动,强行 return lk 会触发编译错误。
- move 后原对象进入空状态,再次调用
lock()或unlock()是未定义行为 - 这种能力常见于资源管理封装、异步任务包装器等需要转移锁所有权的抽象层
- 性能差异几乎为零,但语义错误(比如 move 后还用原对象)会导致难以复现的竞态
真正容易被忽略的点在于:std::unique_lock 的“灵活”不是让你到处手动 lock()/unlock(),而是为特定模式(条件变量、延迟决策、所有权转移)提供必要支撑。多数简单临界区,std::lock_guard 更安全、更清晰、更难写错。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











