C++中mutex互斥锁用法lock_guard与unique_lock区别有哪些实战应用?
- 内容介绍
- 文章标签
- 相关推荐
本文共计852个文字,预计阅读时间需要4分钟。
直接说结论:
什么时候必须用 std::unique_lock?
不是“更高级所以更好”,而是有明确不可替代的使用场景:
-
std::condition_variable::wait()的参数类型强制要求是std::unique_lock——std::lock_guard不能传进去,编译直接报错 - 需要在加锁后做判断,再决定是否继续持有锁(比如双重检查):
lck.unlock()可提前释放,避免锁粒度过大 - 要尝试加锁且不阻塞:
lck.try_lock()或带超时的lck.try_lock_for(),lock_guard没有这些接口 - 要把锁对象作为函数返回值或存进容器(如
std::vector<:unique_lock>></:unique_lock>),因为unique_lock支持移动,lock_guard不支持
std::lock_guard 的典型误用:试图手动 unlock
有人写 guard.unlock() 想提前释放锁,但 std::lock_guard 根本没有 unlock() 成员函数 —— 编译失败。它只在构造时加锁、析构时自动解锁,没别的路可走。
这种设计是故意的:防止你忘记 unlock 或重复 unlock。如果你真需要手动控制,说明你该换 std::unique_lock 了。
立即学习“C++免费学习笔记(深入)”;
常见错误现象:
- 编译错误:
error: 'class std::lock_guard<:mutex>' has no member named 'unlock'</:mutex> - 误以为加了
lock_guard就“安全了”,结果把大量无关逻辑塞进锁作用域,拖慢并发性能
std::unique_lock 的易踩坑点:默认不加锁
和 lock_guard 构造即加锁不同,std::unique_lock 默认构造时不持有锁:
std::mutex mtx; std::unique_lock<std::mutex> lck(mtx, std::defer_lock); // 明确声明不加锁 // 此时 mtx 仍是 unlocked 状态 lck.lock(); // 手动加锁 // ... 临界区 lck.unlock(); // 手动解锁
如果漏写 std::defer_lock,它会像 lock_guard 一样立即加锁;但很多人没意识到这点,又额外调 lck.lock(),导致死锁(同一线程重复 lock 同一个 mutex)。
性能影响:每次调 lock()/unlock() 都有系统调用开销,而 lock_guard 是纯 RAII,无运行时分支,更轻量。
实际选型建议:从需求倒推
别凭感觉选,按这几条快速判断:
- 只保护一段固定代码块,且不需要中途释放 → 用
std::lock_guard - 要和
std::condition_variable配合(比如生产者-消费者)→ 必须用std::unique_lock - 临界区里要先检查条件,满足才继续操作,否则想立刻释放锁 → 用
std::unique_lock+unlock() - 需要 try-lock 或带超时的 lock → 只能用
std::unique_lock
复杂点在于:unique_lock 功能多,但多出来的灵活性也意味着更多出错可能 —— 比如忘了 lock() 导致数据竞态,或 unlock() 后又误用锁对象。简单场景硬上 unique_lock,反而增加维护成本。
本文共计852个文字,预计阅读时间需要4分钟。
直接说结论:
什么时候必须用 std::unique_lock?
不是“更高级所以更好”,而是有明确不可替代的使用场景:
-
std::condition_variable::wait()的参数类型强制要求是std::unique_lock——std::lock_guard不能传进去,编译直接报错 - 需要在加锁后做判断,再决定是否继续持有锁(比如双重检查):
lck.unlock()可提前释放,避免锁粒度过大 - 要尝试加锁且不阻塞:
lck.try_lock()或带超时的lck.try_lock_for(),lock_guard没有这些接口 - 要把锁对象作为函数返回值或存进容器(如
std::vector<:unique_lock>></:unique_lock>),因为unique_lock支持移动,lock_guard不支持
std::lock_guard 的典型误用:试图手动 unlock
有人写 guard.unlock() 想提前释放锁,但 std::lock_guard 根本没有 unlock() 成员函数 —— 编译失败。它只在构造时加锁、析构时自动解锁,没别的路可走。
这种设计是故意的:防止你忘记 unlock 或重复 unlock。如果你真需要手动控制,说明你该换 std::unique_lock 了。
立即学习“C++免费学习笔记(深入)”;
常见错误现象:
- 编译错误:
error: 'class std::lock_guard<:mutex>' has no member named 'unlock'</:mutex> - 误以为加了
lock_guard就“安全了”,结果把大量无关逻辑塞进锁作用域,拖慢并发性能
std::unique_lock 的易踩坑点:默认不加锁
和 lock_guard 构造即加锁不同,std::unique_lock 默认构造时不持有锁:
std::mutex mtx; std::unique_lock<std::mutex> lck(mtx, std::defer_lock); // 明确声明不加锁 // 此时 mtx 仍是 unlocked 状态 lck.lock(); // 手动加锁 // ... 临界区 lck.unlock(); // 手动解锁
如果漏写 std::defer_lock,它会像 lock_guard 一样立即加锁;但很多人没意识到这点,又额外调 lck.lock(),导致死锁(同一线程重复 lock 同一个 mutex)。
性能影响:每次调 lock()/unlock() 都有系统调用开销,而 lock_guard 是纯 RAII,无运行时分支,更轻量。
实际选型建议:从需求倒推
别凭感觉选,按这几条快速判断:
- 只保护一段固定代码块,且不需要中途释放 → 用
std::lock_guard - 要和
std::condition_variable配合(比如生产者-消费者)→ 必须用std::unique_lock - 临界区里要先检查条件,满足才继续操作,否则想立刻释放锁 → 用
std::unique_lock+unlock() - 需要 try-lock 或带超时的 lock → 只能用
std::unique_lock
复杂点在于:unique_lock 功能多,但多出来的灵活性也意味着更多出错可能 —— 比如忘了 lock() 导致数据竞态,或 unlock() 后又误用锁对象。简单场景硬上 unique_lock,反而增加维护成本。

