全网整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:400-708-3566

c++如何实现生产者消费者模型_c++ 互斥锁与条件变量协同工作【案例】

必须用 std::condition_variable 配合 std::mutex 实现生产者消费者模型,因互斥锁无法解决空/满队列时的线程等待问题;wait() 必须在 unique_lock 持有锁时调用,且需用 while 循环或带谓词的重载检查条件,避免虚假唤醒。

std::mutexstd::condition_variable 实现生产者消费者模型,是 C++ 多线程中最典型、也最容易出错的协同场景。核心在于:锁只保护临界区,而条件变量负责“等某个状态成立”,二者必须配合使用,且 wait() 调用前必须已持有锁。

为什么不能只用互斥锁?

互斥锁能阻止多个线程同时访问共享队列,但无法解决“队列空时消费者该干什么”或“队列满时生产者该干什么”的问题。轮询(busy-wait)浪费 CPU;单纯加锁后 return 又会导致线程退出,无法持续工作。这时候必须靠 std::condition_variable 挂起线程,等条件满足再唤醒。

std::condition_variable::wait() 的正确调用姿势

这是最常踩坑的地方:wait() 必须在已锁定的 std::unique_lock<:mutex> 上调用,且内部会自动释放锁并挂起线程;被唤醒后,它会重新获取锁,再返回 —— 但此时条件未必仍成立(可能被其他线程抢走资源),所以必须用 while 循环判断,不能用 if。

  • 错误写法:if (queue.empty()) cv.wait(lock); → 唤醒后不检查,可能读空队列
  • 正确写法:while (queue.empty()) cv.wait(lock);
  • wait() 的第二个参数可以是 lambda,等价于 while + lambda 调用,更简洁:cv.wait(lock, [&]{ return !queue.empty(); });

生产者与消费者的完整协作逻辑

假设共享一个 std::queue,容量上限为 10:

立即学习“C++免费学习笔记(深入)”;

#include 
#include 
#include 
#include 
#include 

std::queue q; std::mutex mtx; std::condition_variable cv_not_full, cv_not_empty; const int MAX_SIZE = 10;

void producer(int id) { for (int i = 0; i < 5; ++i) { std::unique_lock lock(mtx); cv_not_full.wait(lock, [&]{ return q.size() < MAX_SIZE; }); q.push(i + id 10); std::cout << "P" << id << " pushed " << (i + id 10) << ", size=" << q.size() << "\n"; lock.unlock(); // 手动解锁,避免阻塞消费者 cv_not_empty.notify_one(); // 通知至少一个消费者 } }

void consumer(int id) { for (int i = 0; i < 5; ++i) { std::unique_lock lock(mtx); cv_not_empty.wait(lock, [&]{ return !q.empty(); }); int val = q.front(); q.pop(); std::cout << "C" << id << " popped " << val << ", size=" << q.size() << "\n"; lock.unlock(); cv_not_full.notify_one(); // 通知至少一个生产者 } }

注意两点:一是 notify_one() 在锁外调用更安全(避免唤醒后立即竞争锁);二是两个条件变量分别对应“非满”和“非空”,不能共用一个 —— 否则 notify_all() 会同时唤醒所有生产者和消费者,造成无谓竞争。

容易忽略的边界与性能点

实际部署时这几个细节常被跳过:

  • 共享队列未加 std::atomic 或锁保护,直接裸读写 → 数据竞争,UB
  • 忘记在 wait() 的 lambda 中捕获引用(如 [&]),导致读到的是旧值或编译失败
  • std::lock_guard 替代 std::unique_lockwait() 编译不过,因为后者需要支持中途释放/重获锁
  • 生产者 push 后只调用 notify_one(),但如果有多个消费者等待,可能唤醒的是刚处理完的线程,而其他线程还在等 —— 此时用 notify_all() 更稳妥(代价是唤醒开销略高)

真正难的不是写出来,而是想清楚「谁在等什么条件」「谁来通知」「通知之后是否真能满足」—— 这三问没理清,wait() 就会永远挂住,或者频繁虚假唤醒。


# ai  # c++  # 为什么  # 有锁  # if  # while  # 循环  # Lambda  # 线程  # 多线程  # 的是  # 多个  # 互斥  # 挂起  # 这是  # 就会  # 还在  # 一是  # 第二个  # 又会 


相关文章: 定制建站是什么?如何实现个性化需求?  如何用景安虚拟主机手机版绑定域名建站?  Swift开发中switch语句值绑定模式  宝塔建站后网页无法访问如何解决?  外贸公司网站制作,外贸网站建设一般有哪些步骤?  javascript中对象的定义、使用以及对象和原型链操作小结  胶州企业网站制作公司,青岛石头网络科技有限公司怎么样?  如何通过西部数码建站助手快速创建专业网站?  小型网站建站如何选择虚拟主机?  建站之星如何一键生成手机站?  公司网站制作价格怎么算,公司办个官网需要多少钱?  佛山网站制作系统,佛山企业变更地址网上办理步骤?  如何快速搭建安全的FTP站点?  如何快速生成橙子建站落地页链接?  微信小程序 五星评分(包括半颗星评分)实例代码  深入理解Android中的xmlns:tools属性  利用JavaScript实现拖拽改变元素大小  在线教育网站制作平台,山西立德教育官网?  php8.4新语法match怎么用_php8.4match表达式替代switch【方法】  香港服务器建站指南:外贸独立站搭建与跨境电商配置流程  如何用VPS主机快速搭建个人网站?  如何在阿里云ECS服务器部署织梦CMS网站?  制作网站的基本流程,设计网站的软件是什么?  创业网站制作流程,创业网站可靠吗?  建站ABC备案流程中有哪些关键注意事项?  如何在腾讯云免费申请建站?  正规网站制作公司有哪些,目前国内哪家网页网站制作设计公司比较专业靠谱?口碑好?  ,怎么用自己头像做动态表情包?  南宁网站建设制作定制,南宁网站建设可以定制吗?  建站之星代理商如何保障技术支持与售后服务?  如何快速生成ASP一键建站模板并优化安全性?  网站制作公司广州有几家,广州尚艺美发学校网站是多少?  如何在Golang中实现微服务服务拆分_Golang微服务拆分与接口管理方法  建站一年半SEO优化实战指南:核心词挖掘与长尾流量提升策略  如何快速搭建高效服务器建站系统?  如何在Mac上搭建Golang开发环境_使用Homebrew安装和管理Go版本  实例解析Array和String方法  如何用y主机助手快速搭建网站?  合肥做个网站多少钱,合肥本地有没有比较靠谱的交友平台?  制作网站怎么制作,*游戏网站怎么搭建?  ,在苏州找工作,上哪个网站比较好?  交易网站制作流程,我想开通一个网站,注册一个交易网址,需要那些手续?  建站之星IIS配置教程:代码生成技巧与站点搭建指南  如何用5美元大硬盘VPS安全高效搭建个人网站?  小建面朝正北,A点实际方位是否存在偏差?  网站制作的步骤包括,正确网址格式怎么写?  IOS倒计时设置UIButton标题title的抖动问题  建站之星2.7模板快速切换与批量管理功能操作指南  武清网站制作公司,天津武清个人营业执照注销查询系统网站?  如何配置支付宝与微信支付功能? 

您的项目需求

*请认真填写需求信息,我们会在24小时内与您取得联系。