彻底搞懂 C++ 锁、条件变量和生产者-消费者模式
文章目录彻底搞懂 C 锁、条件变量和生产者-消费者模式一、锁mutex到底是什么二、两种锁的区别三、条件变量condition_variable不用条件变量——轮询浪费 CPU用条件变量——数据没到就睡到了自动醒wait 到底做了什么notify_one 和 notify_all为什么 wait 需要一个条件 lambda四、生产者-消费者模式最简单版本为什么两个消费者交替取数据关键细节五、我项目里的 BlockingQueue六、常见面试问题速答Q: lock_guard 和 unique_lock 的区别Q: 条件变量为什么必须配合 while 或 lambdaQ: 为什么 notify 放在 unlock 后面Q: 为什么 push 加锁了pop 的 wait 还能解锁七、我记住的关键规则彻底搞懂 C 锁、条件变量和生产者-消费者模式一、锁mutex到底是什么锁不是代码层面的东西是约定。你和室友住一间房门上挂一把锁——谁进去谁锁门出来再开。另一个人想进去门口等着。#includemutexstd::mutex mtx;// 一把锁void写日志(conststd::stringmsg){mtx.lock();// 锁门// 往文件里写日志这个操作不能同时被两个线程做mtx.unlock();// 开门}如果两个线程同时往同一个文件写日志内容会交叠错乱。加锁后同一时刻只有一个线程在写。但手动lock()/unlock()容易出事——写日志写到一半抛异常了unlock()没执行锁永远开不了所有人卡死。所以 C 提供了管家void写日志(conststd::stringmsg){std::lock_guardstd::mutexlock(mtx);// 构造 立刻加锁// 写日志...}// lock 析构 自动解锁就算中间抛异常也一定解锁lock_guard 构造加锁离开花括号自动解锁。永远用它不要手动调 lock/unlock。二、两种锁的区别lock_guardunique_lock加锁时机构造时自动构造时自动解锁时机离开{}自动离开{}自动也可以手动unlock()能临时解锁再重新加锁吗❌✅能配合条件变量吗❌✅用在哪简单临界区条件变量 / 需要灵活控制的场景// lock_guard省心绝大多数场景用它{std::lock_guardstd::mutexlock(mtx);// 操作共享数据}// unique_lock灵活条件变量必须用它{std::unique_lockstd::mutexlock(mtx);// 操作共享数据lock.unlock();// 提前解锁lock_guard 做不到// 可以做其他不涉及共享数据的操作lock.lock();// 重新加锁}三、条件变量condition_variable锁解决了同时只能一个人访问的问题但解决不了数据还没到先睡会儿的问题。不用条件变量——轮询浪费 CPUwhile(queue_.empty()){std::this_thread::sleep_for(std::chrono::milliseconds(10));// 每隔 10ms 检查一次CPU 空转反应慢}autotaskqueue_.front();queue_.pop();用条件变量——数据没到就睡到了自动醒std::unique_lockstd::mutexlock(mtx);cv.wait(lock,[]{return!queue_.empty();});// 空了就睡0% CPUautotaskqueue_.front();queue_.pop();wait 到底做了什么cv.wait(lock,[]{return!queue_.empty();});① 检查条件 !queue_.empty() → ② true → 直接返回什么都不发生 ③ false → 解锁 lock → 线程挂起睡觉0% CPU ④ 另一个线程调 cv.notify_one() 叫醒我 ⑤ 重新拿锁 → 再检查条件 → true 就取数据关键睡觉前解锁醒了重新拿锁。如果睡觉时还抱着锁不放别人怎么往队列里放东西这就是死锁。notify_one 和 notify_allcv.notify_one();// 叫醒一个正在 wait 的线程cv.notify_all();// 叫醒所有正在 wait 的线程为什么 wait 需要一个条件 lambda因为虚假唤醒——操作系统偶尔会莫名其妙叫醒线程极少见但标准允许。还有一个原因是你通知了一个线程但这期间另一个线程可能先醒、先把数据取走了。所以醒过来第一件事必须是检查条件不满足条件继续睡。四、生产者-消费者模式这是多线程编程里最常见的模式。一个或多个线程生产数据放到队列里一个或多个线程从队列里取数据处理。最简单版本#includecondition_variable#includeiostream#includemutex#includequeue#includethread#includechronostd::mutex mtx;std::condition_variable cv;std::queueintq;boolrunningtrue;voidconsumer(intid){while(running||!q.empty()){std::unique_lockstd::mutexlock(mtx);// 队列空且还在生产 → 睡觉等cv.wait(lock,[]{return!q.empty()||!running;});// runningfalse 且队列空 → 退出if(!runningq.empty())break;intvalq.front();q.pop();lock.unlock();// 数据到手就解锁不拖std::cout消费者id 取走: valstd::endl;std::this_thread::sleep_for(std::chrono::milliseconds(200));}}voidproducer(){for(inti1;i10;i){{std::lock_guardstd::mutexlock(mtx);q.push(i);std::cout生产者 放入: istd::endl;}// 离开 {} 自动解锁cv.notify_one();// 叫醒一个消费者std::this_thread::sleep_for(std::chrono::milliseconds(100));}runningfalse;cv.notify_all();// 叫醒所有人下班了别睡了}intmain(){std::threadc1(consumer,1);std::threadc2(consumer,2);std::threadp(producer);c1.join();// 等线程结束c2.join();p.join();std::cout全部结束std::endl;return0;}运行结果大概是生产者 放入: 1 消费者 1 取走: 1 生产者 放入: 2 消费者 2 取走: 2 生产者 放入: 3 消费者 1 取走: 3 ... 全部结束为什么两个消费者交替取数据因为sleep_for(200ms)让取完数据的消费者暂时不继续抢锁另一个消费者有时间拿到锁。去掉 sleep 就是纯抢锁谁抢到谁拿就不一定交替了。关键细节{std::lock_guardstd::mutexlock(mtx);q.push(i);}// ← 先解锁cv.notify_one();// ← 再通知不要抱着锁通知为什么解锁后才通知如果先通知再解锁——被叫醒的线程立刻尝试拿锁但锁还没解又得等。多此一举。解锁后通知人家醒了直接能拿到锁。五、我项目里的 BlockingQueue项目里把这些东西封装成了一个类templatetypenameTclassBlockingQueue{public:voidpush(T item){{std::lock_guardstd::mutexlock(mutex_);queue_.push(std::move(item));}cv_.notify_one();// 解锁后通知}Tpop(){std::unique_lockstd::mutexlock(mutex_);// 用 unique_lockcv_.wait(lock,[]{return!queue_.empty();});// 空了就睡T itemstd::move(queue_.front());queue_.pop();returnitem;}private:std::mutex mutex_;std::condition_variable cv_;std::queueTqueue_;};用法简单// 生产者odud 的 IPC 接收线程tx_queue_.push(OduSerialTask{...});// 消费者odud 的串口发送线程OduSerialTask tasktx_queue_.pop();// 空了自动睡不占 CPU六、常见面试问题速答Q: lock_guard 和 unique_lock 的区别A: lock_guard 只有构造加锁 析构解锁不能手动控制。unique_lock 可以临时 unlock 再 lock条件变量的 wait 必须用 unique_lock。大部分场景用 lock_guard 就够了。Q: 条件变量为什么必须配合 while 或 lambdaA: 因为虚假唤醒。线程可能被操作系统莫名其妙叫醒也可能被 notify 但数据被另一个线程抢了。醒过来必须重新检查条件不满足继续睡。Q: 为什么 notify 放在 unlock 后面A: 先 unlock 再 notify——被叫醒的线程直接能拿到锁不用再等。反过来先 notify 再 unlock被叫醒的线程发现锁还没解又要等浪费一次上下文切换。Q: 为什么 push 加锁了pop 的 wait 还能解锁A: wait 内部做的事是临时解锁 → 挂起 → 被叫醒 → 重新加锁。push 在 wait 解锁期间可以拿到锁、放数据。这正是条件变量的核心机制。七、我记住的关键规则任何共享数据被多线程读写 → 加锁。用lock_guard别手写 lock/unlock。加锁后不做耗时操作。拿数据就解锁别抱着锁做 IO。生产者-消费者 队列 锁 条件变量。生产者 push notify消费者 pop wait。wait 必须传条件判断。要么 lambda要么 while 套 wait。防止虚假唤醒。先解锁再 notify。不抱着锁通知别人。

相关新闻

27岁,转行网络安全,是这辈子最成功的一件事!27岁开始搞网安好吗?

27岁,转行网络安全,是这辈子最成功的一件事!27岁开始搞网安好吗?

前言 生活就像是一场戏,人在其中扮演的不过是一个个角色,打工人也好,丈夫也罢,儿子也好,父亲也罢。每个人涂上或淡或浓的脂粉,带着千疮百孔的面具,披挂着久经沙场的盔甲,在舞台上挪移…

2026/7/9 3:19:48阅读更多 →
直流负载管理与STM32F303RC优化方案

直流负载管理与STM32F303RC优化方案

1. 直流负载管理的挑战与优化方向在工业控制和电力电子领域,直流负载管理一直是个既基础又关键的技术痛点。我最近在一个自动化产线改造项目中,就深刻体会到了传统方案的局限性——当产线上同时有12台直流电机需要协调控制时,普通的继电器方案…

2026/7/9 3:14:48阅读更多 →
Rainmeter音频可视化终极指南:打造随音乐舞动的桌面频谱

Rainmeter音频可视化终极指南:打造随音乐舞动的桌面频谱

Rainmeter音频可视化终极指南:打造随音乐舞动的桌面频谱 【免费下载链接】rainmeter Desktop customization tool for Windows 项目地址: https://gitcode.com/gh_mirrors/ra/rainmeter 想让你的Windows桌面随着音乐节奏动态变化吗?Rainmeter音频…

2026/7/9 3:14:48阅读更多 →
会议发言人如何自动标注?CAM++ 声纹识别接入AI会议助手实践

会议发言人如何自动标注?CAM++ 声纹识别接入AI会议助手实践

一、引言:会议转写之后,为什么还要标注发言人? 在会议转写系统中,ASR 解决的是“说了什么”的问题。但在真实会议场景里,用户往往还会继续追问: 这句话是谁说的? 如果只有一整段连续转写文本&am…

2026/7/9 4:24:53阅读更多 →
Google Maps数据突然“失真”?Gemini异常检测模块已上线——6小时定位地图偏差根因(附内部诊断清单)

Google Maps数据突然“失真”?Gemini异常检测模块已上线——6小时定位地图偏差根因(附内部诊断清单)

更多请点击: https://kaifayun.com 第一章:Google Maps数据“失真”事件全景速览 2024年夏季,全球多地用户密集报告Google Maps在步行导航、POI(兴趣点)标注及实时路况渲染中出现系统性空间偏移——部分城市街道被整体…

2026/7/9 4:24:53阅读更多 →
第 01 课:模型与消息

第 01 课:模型与消息

第 01 课:模型与消息 配套代码仓库:https://gitee.com/wang-zeyong111/lang-chain-lecture 本课定位 模型与消息是 LangChain 的地基。后面的提示模板、工具调用、Agent、记忆和 LangGraph,都会围绕“消息如何进入模型、模型如何返回消息、…

2026/7/9 4:24:53阅读更多 →
地平线开发岗面试题目

地平线开发岗面试题目

如果你准备投 地平线 的开发岗,最容易踩的坑通常不是不会刷题,而是准备方向不对。 有些公司看起来都在招开发,但真正进面后会发现,关注点完全不一样: 有的公司更看重高并发和分布式 有的公司更看重 C、系统基础和性能…

2026/7/9 4:24:53阅读更多 →
为什么92%的电商团队在SD模型选型上踩坑?(附Shopify/TikTok Shop/拼多多平台适配评分表)

为什么92%的电商团队在SD模型选型上踩坑?(附Shopify/TikTok Shop/拼多多平台适配评分表)

更多请点击: https://kaifayun.com 第一章:为什么92%的电商团队在SD模型选型上踩坑? 电商团队在部署 Stable Diffusion(SD)模型时,常因“开箱即用”假象陷入系统性误判——看似只需下载模型权重即可生成商…

2026/7/9 4:24:53阅读更多 →
OpenResty 1.25.3.1 升级实战:CentOS 7.9 平滑迁移与 5 个 Lua 模块配置验证

OpenResty 1.25.3.1 升级实战:CentOS 7.9 平滑迁移与 5 个 Lua 模块配置验证

OpenResty 1.25.3.1 生产环境升级指南:CentOS 7.9 平滑迁移与核心模块验证1. 升级前的战略准备在开始升级前,我们需要建立一个完整的风险评估和回滚方案。不同于全新安装,生产环境的升级需要特别关注业务连续性保障。以下是关键准备步骤&…

2026/7/9 4:19:52阅读更多 →
从GitHub安全案例解析常见漏洞与防护实践

从GitHub安全案例解析常见漏洞与防护实践

1. 项目概述:从GitHub Trending看安全实战 最近在GitHub Trending上看到一个项目,叫 skills4/skills ,它因为一些安全漏洞案例被大家讨论。这其实是一个挺典型的场景:一个旨在展示或教授某种技能的仓库,本身却成了安…

2026/7/8 5:12:14阅读更多 →
MLT 2026启示:因果推理与概率建模驱动下一代LLM应用

MLT 2026启示:因果推理与概率建模驱动下一代LLM应用

# MLT 2026启示:因果推理与概率建模驱动下一代LLM应用## 一、背景与挑战:从“黑箱预测”到“可信推理”2026年6月,第7届机器学习与趋势国际会议(MLT 2026)将在悉尼召开。会议议程中,“因果与可解释机器学习…

2026/7/8 7:00:12阅读更多 →
通达OA SQL注入漏洞深度剖析:从手工注入到自动化利用与防御

通达OA SQL注入漏洞深度剖析:从手工注入到自动化利用与防御

1. 项目概述与漏洞背景最近在梳理一些历史OA系统的安全风险时,通达OA v11.6版本中的一个老漏洞又进入了我的视线。这个漏洞位于/general/bi_design/appcenter/report_bi.func.php文件中,是一个典型的SQL注入点。虽然这个漏洞的利用方式看起来并不复杂&am…

2026/7/9 2:47:07阅读更多 →
Three.js 着色器光效教程

Three.js 着色器光效教程

着色器光效 Shader Light ▶ 在线运行案例 案例合集: 三维可视化功能案例(threehub.cn)开源仓库github地址: https://github.com/z2586300277/three-cesium-examples400个案例代码: 网盘链接 你将学到什么 ShaderMaterial 自…

2026/7/9 0:04:37阅读更多 →
如何5分钟掌握CS2智能库存管理:开源工具CASEMOVE终极指南

如何5分钟掌握CS2智能库存管理:开源工具CASEMOVE终极指南

如何5分钟掌握CS2智能库存管理:开源工具CASEMOVE终极指南 【免费下载链接】casemove A dedicated desktop app that enables you to move items in and out of storage units in CS2. 项目地址: https://gitcode.com/gh_mirrors/ca/casemove 还在为CS2存储单…

2026/7/9 0:04:37阅读更多 →
GBase 8a vs MySQL 8.0:ALTER TABLE语法与限制的5点关键差异对比

GBase 8a vs MySQL 8.0:ALTER TABLE语法与限制的5点关键差异对比

GBase 8a与MySQL 8.0:ALTER TABLE语法差异深度解析与实战指南1. 两种数据库的ALTER TABLE能力全景对比在数据库架构设计和运维过程中,表结构变更(DDL操作)是不可避免的需求。GBase 8a作为国产分析型数据库代表,与开源M…

2026/7/9 0:04:37阅读更多 →
YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

如果你在部署 YOLOv8 时,发现推理速度只有可怜的 1-2 FPS,而别人的演示视频却能跑到 30 FPS 以上,那么问题很可能不在模型本身,而在于你的整个处理链路。很多开发者拿到一个训练好的 YOLOv8 模型后,会直接使用官方示例…

2026/7/8 6:59:54阅读更多 →
Coze与Dify对比指南:低代码AI应用开发从入门到实战

Coze与Dify对比指南:低代码AI应用开发从入门到实战

1. 从零到一:为什么你需要了解 Coze 和 Dify?如果你对 AI 应用开发感兴趣,但一看到“大模型”、“智能体”、“工作流”这些词就头疼,觉得门槛太高,那这篇文章就是为你准备的。很多开发者,包括我自己&#…

2026/7/8 13:42:39阅读更多 →
AI生图工具怎么选?2026年6月版实测对比

AI生图工具怎么选?2026年6月版实测对比

做自媒体的朋友应该都有体会:配图一直是个让人头疼的问题。2026年,AI生图工具已经非常成熟了,但工具太多反而不知道怎么选。以下是截至2026年6月我对主流AI生图工具的实测对比。Midjourney V8.1:速度之王2026年6月11日&#xff0c…

2026/7/8 13:42:39阅读更多 →