std::unique_lock可手动控制锁生命周期,支持延迟加锁、提前解锁、转移所有权及条件变量配合;std::lock_guard仅构造加锁、析构释放,不可转移且功能受限。

std::unique_lock 能手动控制锁的生命周期,std::lock_guard 不能
它俩都是 RAII 锁包装器,但 std::unique_lock 支持延迟加锁、提前解锁、转移所有权、配合条件变量——std::lock_guard 只能在构造时加锁、析构时自动释放,除此之外啥也不能干。
常见错误现象:std::lock_guard 被误用于需要“先检查再加锁”或“加锁后分段处理”的场景,结果逻辑卡死或竞态复现。
- 需要
std::unique_lock的典型场景:用std::condition_variable::wait()、实现 try-lock、在函数内部分支加锁/解锁、把锁传给另一个函数 -
std::lock_guard更轻量(无状态、无虚函数、通常不带成员指针),编译期开销更低;std::unique_lock多一个指针 + 状态位,但换来的是灵活性 - 别以为
std::unique_lock一定更慢——现代实现下,若全程不调用unlock()或release(),它的性能和std::lock_guard几乎一致
std::unique_lock 构造时不加锁?得看构造函数参数
它有多个构造重载,关键区别就在要不要立即调用 mutex.lock():
-
std::unique_lock<:mutex> lk(mtx)</:mutex>→ 构造即加锁(等价于mtx.lock()) -
std::unique_lock<:mutex> lk(mtx, std::defer_lock)</:mutex>→ 不加锁,后续可手动lk.lock()或lk.try_lock() -
std::unique_lock<:mutex> lk(mtx, std::try_to_lock)</:mutex>→ 非阻塞尝试加锁,失败时lk.owns_lock() == false -
std::unique_lock<:mutex> lk(mtx, std::chrono::milliseconds(10))</:mutex>→ 带超时的尝试加锁(C++14 起)
容易踩的坑:std::defer_lock 是字面量,不是类型,别写成 std::defer_lock_t;也别漏掉命名空间,否则编译报错:error: ‘defer_lock’ was not declared in this scope。
立即学习“C++免费学习笔记(深入)”;
std::unique_lock 可以 move,std::lock_guard 不行
因为 std::unique_lock 显式定义了移动构造函数和移动赋值运算符,而 std::lock_guard 删除了它们(禁用拷贝和移动)。
这意味着你可以把锁“交给”另一个作用域,比如:
std::unique_lock<std::mutex> acquire_lock(std::mutex& m) {
return std::unique_lock<std::mutex>(m, std::defer_lock);
}
// ……之后在别处调用 .lock()
但下面这段代码是非法的:
std::lock_guard<std::mutex> lg(mtx); auto lg2 = std::move(lg); // 编译失败:use of deleted function
性能影响不大,但设计意图很明确:std::lock_guard 绑定到当前作用域,绝不外泄;std::unique_lock 是可转移的资源句柄。
和条件变量搭配时,必须用 std::unique_lock
std::condition_variable::wait() 和 wait_for() 的第一个参数强制要求是 std::unique_lock<:mutex>&</:mutex>,传 std::lock_guard 直接编译不过。
原因很实在:wait 必须在挂起线程前原子地释放锁,并在线程唤醒后重新获取锁——这个“释放+重获”的过程需要锁对象能被临时置空并恢复,只有 std::unique_lock 支持 release() 和内部状态管理。
- 别试图绕过:写个 wrapper 把
std::lock_guard强转成std::unique_lock?不行,类型不兼容 - 别省事:在
wait()前手动unlock()、唤醒后再lock()?会引入竞态,标准库不保证唤醒和加锁之间的原子性 - 一个典型错误模式:用
std::lock_guard加锁后调cv.notify_one()没问题,但只要涉及wait,就必须换std::unique_lock
真正容易被忽略的点是:即使你没显式调 unlock(),std::unique_lock 在 wait() 中的内部释放也是安全的——前提是它确实持有锁(owns_lock() == true),否则行为未定义。










