std::lock_guard不可拷贝只可移动,因其遵循RAII独占所有权原则:拷贝会导致重复解锁或漏解锁;它删除了拷贝构造与赋值函数,仅保留移动构造(C++17起noexcept),但实践中极少移动,通常直接在作用域内构造。

std::lock_guard 为什么不能拷贝,只能移动?
因为 std::lock_guard 的设计目标是「RAII + 独占所有权」:构造时加锁,析构时自动解锁。如果允许拷贝,两个对象就可能同时持有同一把锁,析构时重复解锁(或漏解锁),引发未定义行为。
它删掉了拷贝构造函数和拷贝赋值运算符,只保留了移动构造(C++17 起默认为 noexcept)——但实际中几乎不用移动,因为 std::lock_guard 通常在作用域内直接构造,生命周期由作用域控制。
- 错误写法:
std::lock_guard<:mutex> g1(m); auto g2 = g1;</:mutex>→ 编译失败,提示 “use of deleted function” - 正确姿势:始终在需要同步的代码块开头直接构造,不赋值、不传参(除非用引用或指针间接传递锁对象本身)
std::lock_guard 和 std::unique_lock 该选谁?
std::lock_guard 是轻量、一次性、不可延期的锁管理器;std::unique_lock 更灵活,支持延迟加锁、条件变量配合、手动解锁/重锁、以及可转移所有权。
如果你只需要“进作用域就锁、出作用域就放”,std::lock_guard 更合适:开销更小、意图更清晰、不易误用。
立即学习“C++免费学习笔记(深入)”;
- 用
std::lock_guard:保护临界区读写、简单互斥访问共享计数器等 - 别用
std::lock_guard:要配合std::condition_variable::wait()、需提前释放锁再做耗时操作、或多把锁需要按顺序尝试获取(这时应考虑std::scoped_lock或std::try_lock) - 性能差异:
std::lock_guard构造即调用mtx.lock(),无额外状态;std::unique_lock多一个布尔标记位和空构造函数支持,微小开销但可接受
常见误用:在 if 分支或循环里构造 lock_guard 却忘了作用域边界
std::lock_guard 的生命周期完全绑定于其声明所在的作用域。很多人写成这样:
if (cond) {
std::lock_guard<std::mutex> g(m);
shared_data++;
} // ← 正确:这里自动解锁
// 但如果漏掉大括号……
if (cond)
std::lock_guard<std::mutex> g(m); // ← 错!g 是临时对象,构造完立即析构
shared_data++; // ← 竟然没锁住!
编译器不会报错,但逻辑彻底失效。C++ 中单行 if 后跟无括号语句,其后所有代码都不属于 if 分支。
- 永远显式加
{ },哪怕只有一行 - 不要依赖“一行缩进”来判断作用域
- 现代编辑器或 linter(如 clang-tidy 的
readability-braces-around-statements)可以捕获这类问题
std::lock_guard 能否用于递归锁?
可以,但要注意类型匹配:std::lock_guard 本身不关心锁是否递归,它只调用 lock() 和 unlock()。只要底层 std::mutex 替换为 std::recursive_mutex,就能正常工作。
不过,递归锁往往暗示设计复杂度上升——多数情况下应优先重构,避免同一线程多次进入同一临界区。
- 合法写法:
std::lock_guard<:recursive_mutex> g(rec_mtx);</:recursive_mutex> - 不推荐场景:在函数 A 中加锁后调用函数 B,而 B 又试图对同一锁加锁 → 容易掩盖锁粒度不合理的问题
- 兼容性注意:POSIX 的
pthread_mutex_t默认非递归;Windows 的CRITICAL_SECTION默认是递归的,但 C++ 标准库不直接暴露它
std::lock_guard 很快,但决定“哪几行该被锁住”才花掉你 90% 的调试时间。


















