彻底搞懂 C++ 锁、条件变量和生产者-消费者模式

发布时间:2026/8/28 0:07:25
彻底搞懂 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。不抱着锁通知别人。

相关新闻

别再被忽悠了!找网站建设酷万网络前,这3个坑你必须知道

别再被忽悠了!找网站建设酷万网络前,这3个坑你必须知道

网站上线三个月,访客还是两位数,钱打水漂连个响儿都听不见。这种焦虑,我懂。很多老板花大几万做个网站,结果打开一看,加载慢得像蜗牛,手机上看字小得眯着眼,百度搜都搜不到。不是技术不行,是思路错了。记得去年有个做建材的朋友,找我吐槽。他说之前找的那家,报价便宜…

发布时间:2026/8/20 3:31:59
别被忽悠了!揭秘盛世阳光网站建设背后的真相与坑

别被忽悠了!揭秘盛世阳光网站建设背后的真相与坑

说实话,做网站这行水太深了。深到你根本想象不到。很多老板花了几万块,最后拿到一个连手机都打不开的网页。真的气人。今天我就把话撂这儿,关于盛世阳光网站建设,咱们得聊聊真东西。别再听那些销售吹什么“全网最低价”。低价没好货,这是铁律。我见过太多案例,前期报价三…

发布时间:2026/8/20 3:31:59
建设工程图审管理信息系统网站怎么选?避坑指南与真实使用体验

建设工程图审管理信息系统网站怎么选?避坑指南与真实使用体验

建设工程图审管理信息系统网站怎么选?别被花哨的功能迷了眼。核心就看三点:流程顺不顺、资料安不安全、响应快不快。这篇不整虚的,直接说大实话。以前做项目的时候,最头疼的就是图纸审查。纸质版寄来寄去,丢件是常事。找起来像大海捞针。后来公司换了系统,确实省心不少。…

发布时间:2026/8/20 3:32:00
跑断腿?我在平阳县建设局网站办证的血泪史与避坑指南

跑断腿?我在平阳县建设局网站办证的血泪史与避坑指南

说实话,以前我对“跑审批”这四个字充满了恐惧。总觉得那是只有大企业或者专业中介才能玩转的游戏,咱们普通小老板或者刚入行的工程人,根本摸不着头脑。直到上个月,我为了一个小型装修项目的施工许可,硬着头皮去了一趟平阳县建设局网站,结果发现,只要找对路子,这事儿真…

发布时间:2026/8/27 2:14:02
为什么你的网站留不住人?揭秘建设网站会员体系的底层逻辑与实操指南

为什么你的网站留不住人?揭秘建设网站会员体系的底层逻辑与实操指南

很多老板都在问:为什么我的网站流量不少,转化率却惨不忍睹?其实,问题往往出在“留客”上。你花了大价钱买流量,用户进来逛了一圈,连个招呼都没打就走了。这就像开了一家实体店,顾客进门看看,然后转身离开,你连个联系方式都没拿到。这种“一次性买卖”思维,在今天的互…

发布时间:2026/8/26 16:46:12
徐州市丰县建设局网站 咋用才不踩坑?老业主掏心窝子分享

徐州市丰县建设局网站 咋用才不踩坑?老业主掏心窝子分享

昨晚半夜两点,我还在盯着手机屏幕,心里那个急啊。为啥?因为我家那套安置房的事儿,开发商那边一直拖泥带水,说是等公示,可公示啥样我心里没底。没办法,只能硬着头皮去查“徐州市丰县建设局网站”。说实话,第一次上去的时候,我整个人是懵的。界面那叫一个复古,跟咱们老…

发布时间:2026/8/26 16:46:10
龙口网站建设公司哪家好?别踩坑,看这几点就够了

龙口网站建设公司哪家好?别踩坑,看这几点就够了

本文关键词:龙口网站建设公司哪家好做企业官网,最怕啥?怕花了几万块,结果打开慢得像蜗牛,手机端还乱码。更怕的是,搜“龙口某某公司”,首页连个影子都找不着。钱打水漂,还耽误事。很多老板找我聊,开口就问:“龙口网站建设公司哪家好?”这话问得实在。毕竟龙口这地方…

发布时间:2026/8/26 16:46:11
别再被忽悠了!一份真正落地的建筑网站建设方案,专治各种花里胡哨

别再被忽悠了!一份真正落地的建筑网站建设方案,专治各种花里胡哨

说实话,我见过太多建筑公司的官网了。真的,多到让人想吐。要么就是满屏的大图,加载慢得像蜗牛。要么就是文案写得云里雾里,根本不知道你是干啥的。客户点进来三秒钟,啪,关掉了。这就叫浪费生命。今天我不讲那些虚头巴脑的理论。我就想聊聊,到底怎么做一个真正能接活的建…

发布时间:2026/8/27 15:36:06
个人做计算机编程与网站建设到底难不难?老程序员掏心窝子说几句

个人做计算机编程与网站建设到底难不难?老程序员掏心窝子说几句

这篇文章不讲那些虚头巴脑的理论,直接告诉你新手入坑计算机编程与网站建设最真实的坑在哪,以及怎么避开。很多人以为写代码就是对着黑屏幕敲字母,其实那是电影骗人的。真正的难点在于怎么把脑子里的想法变成别人能看懂、能用的网页。如果你正纠结要不要学,或者刚起步觉得头…

发布时间:2026/8/27 15:36:41