std::unique_lock比std::lock_guard更灵活但更易出错:前者支持延迟加锁、手动锁控、所有权转移及配合条件变量,后者仅适用于构造即锁、析构即锁的简单场景。

std::unique_lock 是 c++11 引入的可移动(moveable)、可延迟加锁、可手动控制加锁/解锁时机的互斥量包装器,比 std::lock_guard 更灵活,但也更易出错。
核心区别:灵活性 vs 简单性
两者都用于 RaiI 方式管理互斥量(如 std::mutex),但设计目标不同:
- std::lock_guard:构造即加锁,析构即解锁,不可复制、不可移动,生命周期绑定严格,适合“进作用域就锁、出作用域就放”的简单场景。
- std::unique_lock:构造时可选择不加锁(
std::defer_lock),支持手动调用lock()/unlock(),支持转移所有权(move),还能配合std::condition_variable等高级同步原语。
常见使用场景对比
用 lock_guard 的时候: 只在某段代码块内保护临界区,不需要中途释放锁、也不需要条件等待。
std::mutex mtx; void safe_increment() { std::lock_guard<std::mutex> lk(mtx); // 构造即锁 ++counter; // 临界区 } // 析构自动解锁 —— 简洁安全
必须用 unique_lock 的时候:
- 需要延迟加锁(比如先做检查再决定是否加锁)
- 要和
std::condition_variable::wait()配合(wait 要求锁可手动释放并重获) - 需要在函数内多次加锁/解锁(例如分阶段处理)
- 要把锁的所有权转移到另一个作用域(如返回一个带锁的资源)
典型 unique_lock 用法示例
✅ 延迟加锁:
立即学习“C++免费学习笔记(深入)”;
std::unique_lock<std::mutex> lk(mtx, std::defer_lock); if (need_to_modify()) { lk.lock(); // 按需加锁 update_data(); lk.unlock(); // 可提前释放 } // lk 析构时若仍持有锁,会自动释放
✅ 配合条件变量:
std::mutex mtx; std::condition_variable cv; bool ready = false; // 等待线程: std::unique_lock<std::mutex> lk(mtx); cv.wait(lk, []{ return ready; }); // wait 内部会自动 unlock + 重新 lock // 继续执行时 lk 一定已重新加锁
注意事项
- 不要对同一个
unique_lock多次调用lock()(未解锁时再 lock 会死锁) - 手动
unlock()后不要再访问被保护的数据,除非你明确知道其他线程不会修改它 - 避免把
unique_lock当成“更高级的 lock_guard”滥用 —— 复杂性带来维护成本 - 如果只是保护一段短小临界区,优先选
lock_guard;只有真需要它的特性时才用unique_lock
基本上就这些。选哪个不是看“谁更新”,而是看“你的同步逻辑是否需要它提供的控制力”。