当前位置: 首页 > news >正文

lock_guard 与 unique_lock:一对“保安”与“管家”的互斥锁双人舞

区别和联系

lock_guard 是轻量级保安,只管进门锁门;unique_lock 是万能管家,还能临时开门、换房、陪条件变量聊天。

1. 核心差异(表格速查)

特性 lock_guard<Mutex> unique_lock<Mutex>
自动加/解锁
手动中途 unlock() lock()
延迟加锁 (std::defer_lock)
condition_variable 合作 (必需)
所有权转移 (std::move)
对象大小 / 开销 最小(≈1 指针) 略大(多状态标志)

2. 典型场景对照

std::mutex m;// 1. 简单临界区——lock_guard 足够
void simple() {std::lock_guard<std::mutex> lk(m);   // 构造即锁,析构即解/* 临界区 */
}// 2. 条件变量——必须用 unique_lock
void waiting_thread() {std::unique_lock<std::mutex> lk(m);  // 可以晚点锁、中途解cv.wait(lk, []{ return ready; });    // 内部会解锁→等待→再锁
}// 3. 提前解锁以减少粒度
void reduce_lock_time() {std::unique_lock<std::mutex> lk(m);/* 必须受保护的操作 */lk.unlock();        // 提前解锁,让其他线程尽早进入/* 其余耗时但无需锁的工作 */
}                       // 若忘解,这里仍会自动解

3. 联系与最佳实践

  • 底层资源相同:都仅对一把 mutex 做引用,不拥有它;生命周期内保证 mutex 被正确地 unlock()
  • 可混用:同一线程对同一 mutex 先 unique_locklock_guard 是安全的(前提是自己已持锁)。
  • 性能敏感且逻辑简单 → 优先 lock_guard;需要条件变量、延迟锁、手动解锁、所有权转移 → 用 unique_lock
  • 现代 C++ 更推荐 作用域锁std::scoped_lock(C++17)可一次性锁多个 mutex,避免死锁,是 lock_guard 的多锁升级版。

4. 小结

简单锁事 guard 办;
条件等待unique 管;
中途开关需灵活unique 全能干.

unique_lock 在condition_variable中的典型应用

一个生产者线程往队列里放数据,一个消费者线程用条件变量等待数据;std::unique_lock 全程配合 cv.wait(),实现零 CPU 空转的阻塞式消费。
(直接 g++ -std=c++17 file.cpp -pthread 就能跑)

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <queue>
#include <chrono>std::queue<int> q;                 // 共享缓冲区
std::mutex m;                      // 保护 q
std::condition_variable cv;        // 通知“队列非空”
bool done = false;                 // 生产结束标记/* ========== 生产者 ========== */
void producer()
{for (int i = 1; i <= 5; ++i) {std::this_thread::sleep_for(std::chrono::milliseconds(200)); // 模拟耗时{std::lock_guard<std::mutex> lk(m);   // 简单临界区,用 guard 即可q.push(i);std::cout << "[producer] push " << i << '\n';}cv.notify_one();                           // 通知消费者}// 生产完毕{std::lock_guard<std::mutex> lk(m);done = true;}cv.notify_one();                               // 最后一次唤醒
}/* ========== 消费者 ========== */
void consumer()
{while (true) {std::unique_lock<std::mutex> lk(m);        // 1. 先加锁// 2. 等待条件成立(队列非空 或 生产结束)cv.wait(lk, []{ return !q.empty() || done; });// 3. 被唤醒后仍持有锁,可以安全访问队列if (!q.empty()) {int val = q.front();q.pop();std::cout << "[consumer] pop  " << val << '\n';lk.unlock();                             // 4. 提前解锁,减少锁粒度/* 模拟耗时处理 */std::this_thread::sleep_for(std::chrono::milliseconds(300));} else if (done) {std::cout << "[consumer] finished\n";break;                                   // 队列空且生产结束,退出}}
}/* ========== main ========== */
int main()
{std::thread t1(producer);std::thread t2(consumer);t1.join();t2.join();return 0;
}

运行示例输出

[producer] push 1
[consumer] pop  1
[producer] push 2
[consumer] pop  2
[producer] push 3
[consumer] pop  3
[producer] push 4
[consumer] pop  4
[producer] push 5
[consumer] pop  5
[consumer] finished

疑问一:cv.wait(lk, []{ return !q.empty() || done; });好像没有什么用?

当最后一次 cv.notify_one();的时候,consumer会立刻执行,因为队列这个时候不是为空,原本consumer执行完后,要继续“睡”,但是因为done 赋值为true了以后,导致consumer又被唤醒,然后告诉consumer“下班了,后面没有了”,用来结束整个while

疑问二:这段代码中 cv 不是**必须**的,但它的存在有优化作用。让我们分析一下:

没有 cv 时会发生什么?

如果去掉条件变量,消费者线程会变成忙等待(busy-waiting):

while (true) {std::lock_guard<std::mutex> lk(m);if (!q.empty()) {// 处理数据...} else if (done) {break;}// 没有数据时:解锁 -> 立即重新加锁检查 -> 循环...
}

这会带来两个问题:

  1. CPU 浪费:消费者会频繁地加锁/解锁,不断检查队列状态
  2. 响应延迟:即使使用 sleep,也需要在等待时间和响应速度之间权衡

cv 时的优势

cv.wait(lk, predicate) 实现了:

  1. 高效等待:在等待期间释放锁,让生产者能访问队列
  2. 避免忙等待:线程被挂起,不消耗 CPU
  3. 即时响应notify_one() 能立即唤醒等待的线程

改进建议

你的直觉是对的,当前代码可以更简洁。这是一个更清晰的版本:

void consumer()
{while (true) {std::unique_lock<std::mutex> lk(m);// 等待条件成立cv.wait(lk, []{ return !q.empty() || done; });// 处理所有可用数据while (!q.empty()) {int val = q.front();q.pop();lk.unlock();  // 提前解锁std::cout << "[consumer] pop  " << val << '\n';std::this_thread::sleep_for(std::chrono::milliseconds(300));lk.lock();    // 重新加锁检查队列}if (done && q.empty()) {std::cout << "[consumer] finished\n";break;}}
}

总结

  1. cv.wait(lk, pred) 内部会原子地解锁→阻塞→被唤醒后加锁,这是 lock_guard 做不到的,因此必须用 unique_lock
  2. 消费完数据后 lk.unlock(),让生产者线程尽快拿到锁继续生产,降低临界区长度。
  3. done 标志 + cv 保证消费者在生产结束后不会无限等待。
方案 优点 缺点
有 cv 高效、低 CPU、响应快 代码稍复杂
无 cv(忙等待) 代码简单 浪费 CPU、可能响应慢

结论:对于生产-消费模式,条件变量是最佳实践,但不是语法必需。在小例子中可能不明显,但在实际高并发、高性能系统中,条件变量能显著提升效率和响应性。

http://www.jsqmd.com/news/60602/

相关文章:

  • PbootCMS附件上传失败报错“UNKNOW: Code: 8192”的解决方法
  • 寒冬保暖羽绒被怎么选?这 5 个专业品牌带你解锁温暖睡眠新体验
  • 2025进口地板十大品牌综合实力排行榜 - 智能、环保与品质的巅峰对决
  • 2025年12月真空袋厂家综合推荐榜:行业趋势与实操选择要点
  • GODIAG GT327 SUPER DOIP ENET OBDII Scanner - BT4 Voltage Display for iOS/Android/Windows
  • 备婚 _ 结婚 _ 陪嫁 _ 乔迁必看!重要时刻5 大高性价比羽绒被品牌推荐
  • 基于势场法的多智能体机器人编队控制
  • 2025年12月真空袋厂家选择指南:权威维度横向评测解析
  • 2025年工业冷风机在制造业应用实现重大突破,铁皮房车间厂房降温/装配车间通风降温/大型钢结构车间降温工业冷风机生产厂家哪家好
  • pbootcms内页子栏目当前栏目如何实现高亮显示(PbootCMS 内页子栏目当前栏目高亮显示的实现方法)
  • 儿童羽绒被怎么选?这 5 个口碑品牌帮你守护孩子温暖睡眠
  • 2026年X射线防护服源头厂家推荐:射线防护服/铅衣防护服/医用 x 射线防护服公司品牌推荐
  • 从 1000 到 4000 元,5 款高性价比羽绒被品牌解析:技术流博主帮你划重点
  • 2025年市场认证机构推荐:哪家权威性更高?全方位评测与用户口碑分析
  • 2025年12月背单词软件评测排名:从功能到口碑的深度解析
  • ANSYS 2025 R1 仿真算力升级 电子研发提速!ANSYS 2025 R1 下载安装教程 新版本核心优势速递
  • 2025年靠谱的电动缸源头工厂五大推荐,电动缸工厂选哪家全解
  • 2025停经架品牌制造商TOP5权威推荐:甄选的停经架厂家,
  • 2025宁波奢侈品回收TOP5推荐:看哪家奢侈品回收门店可靠
  • 2025年销量领先认证机构推荐:哪家更值得信赖?多维评估与案例比对
  • 2025年度商用净水器服务商TOP5权威推荐:专业选型指南,
  • 2025年12月背单词软件推荐榜:五大工具深度对比与选择指南
  • 2025靠谱的冷水机厂家TOP5权威推荐:化工冷水机核心选型
  • 2025年全球贵金属交易平台TOP5权威测评:EETRADE
  • 2025年银川专业祛痣机构排名推荐,易己弘美容详细介绍、研发
  • 深入解析:CMake下载和安装教程(附安装包)
  • LINQ查询表达式基础
  • 2025 年羽绒蓬松度天花板!这 5 家羽绒供应商凭什么征服高端市场?
  • 狂揽 19000+ Star 的国产开源项目
  • 2025除尘羽绒工厂怎么选?这 5 家实力派企业值得关注