std::unique_lock支持延迟加锁、配合condition_variable使用、可手动解锁、支持移动语义,但需谨慎管理锁状态以避免竞态或崩溃。

std::unique_lock 支持延迟加锁(std::defer_lock)
当你需要「先判断条件,再决定是否加锁」时,std::lock_guard 会立刻加锁,哪怕后续根本不会进入临界区——这浪费了锁持有时间,还可能阻塞其他线程。std::unique_lock 配合 std::defer_lock 可以把加锁动作推迟到真正需要时:
- 构造时不加锁:
std::unique_lock<:mutex> ulk(mtx, std::defer_lock);</:mutex> - 检查条件后再调用
ulk.lock()或ulk.try_lock() - 如果条件不满足,完全不加锁,避免无谓争抢
典型场景:保护一个非空队列消费逻辑,避免在空队列上长期持锁。
std::unique_lock 必须用于 std::condition_variable::wait()
std::condition_variable::wait() 的签名是 wait(unique_lock<mutex>&)</mutex>,它内部会先调用 unlock() 释放锁,挂起线程,等被唤醒后再重新加锁。这个「手动解锁 → 等待 → 重锁」流程,std::lock_guard 根本无法支持。
- 错误写法:
std::lock_guard<:mutex> lg(mtx); cv.wait(lg);</:mutex>→ 编译失败 - 正确写法:
std::unique_lock<:mutex> ulk(mtx); cv.wait(ulk);</:mutex> - 注意:
wait()返回时,ulk已自动重新加锁,无需额外操作
std::unique_lock 允许中途手动解锁(unlock())
临界区内若要调用可能长时间阻塞的函数(如 read()、send()、sleep()),继续持锁会严重降低并发度。这时可以主动释放锁:
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
立即学习“C++免费学习笔记(深入)”;
- 调用
ulk.unlock()后,ulk.owns_lock()返回false,但对象仍有效 - 之后还能调用
ulk.lock()重新加锁(前提是互斥量没被其他线程占用) - 常见误用:对已释放锁的
ulk再次unlock()—— 行为未定义,但通常不会崩溃;更危险的是对默认构造的空unique_lock调用lock(),会直接崩溃
std::unique_lock 支持移动语义,可用于返回或传递
std::lock_guard 不可拷贝也不可移动,生命周期必须严格绑定在当前作用域内。而 std::unique_lock 支持移动,适合封装成工具函数或跨作用域转移锁所有权:
- 例如:写一个函数,尝试加锁并返回带锁状态的
unique_lock,调用方决定是否继续使用 - 移动后原对象变为“空状态”,
owns_lock()为false,不能再调用lock()或unlock() - 不能复制:试图
auto lk2 = lk1;会编译失败,这是设计使然,防止锁所有权歧义
真正容易被忽略的是:std::unique_lock 的灵活性不是免费的——它要求你明确管理锁的状态(是否已持有、是否为空),稍一疏忽(比如忘了检查 owns_lock() 就调用 unlock())就会引入难以复现的竞态或崩溃。别把它当成 lock_guard 的“升级版”来无脑替换。

















