【C++多线程】C++11互斥锁和条件变量实现生产者消费者模型

先看几个问题,第三个问题可以先看代码然后再理解

Q1:临界区在哪

A1: 队列中元素在「生产者生产(push)」和「消费者消费(pop)」时就是临界区

Q2:同步操作在哪

A2: 很显然,队列只有在存在元素的前提下消费者才能消费,队列中元素满(假设有容量线程)时只有生产者是不能生产的,因此

  • 生产者队列满了就应该通知消费者消费
  • 消费者线程发现队列为空就需要通知生产者线程先生产物品

Q3:为什么消费者在 cv.wait(lck) 的条件是 while 而不是 if

A3: cv.wait(lock) 本质上是阻塞的,它会一直等待,直到接收到 notify 或 notify_all 的通知。但是,在某些情况下,虽然没有收到通知,但 cv.wait(lock) 可能会返回。这种情况被称为虚假唤醒(spurious wakeup)。

虚假唤醒是因为条件变量的实现方式,以及底层操作系统和硬件的影响。条件变量的实现通常依赖于底层的线程库和操作系统,它们可能在某些情况下引发虚假唤醒,这是一种难以避免的情况。

因此,为了编写健壮的多线程代码,通常建议使用循环来检查条件,就像在前面的示例中使用的 while (dataQueue.empty()) 一样。这样,即使发生虚假唤醒,线程也会再次检查条件,确保只有在条件满足时才继续执行。

总之,虽然 cv.wait(lock) 通常是阻塞的,但在多线程环境中,考虑到虚假唤醒是一种良好的编程实践,以确保正确性和可靠性。

总结来说就是 cv.wait(lock)可能会在某些情况下(如操作系统调度或硬件中断等)自行返回。因此,为了保险起见,应该在一个循环中检查条件,如示例中使用的 while (dataQueue.empty()),以防止虚假唤醒导致的错误行为

完整代码如下:

#include <iostream>
#include <queue>
#include <thread>
#include <memory>
#include <condition_variable>using namespace std;std::mutex mtx; // 互斥锁实现线程之间的互斥操作
std::condition_variable cv; // 条件变量实现线程之间通信操作class Queue {public:void put(int val) {unique_lock<std::mutex> lck(mtx);if (q.size() == 10) {// 生产者队列满了就应该通知消费者消费// 生产者线程应该进入 #1 等待状态,并且 #2 把 mtx 释放掉cv.wait(lck);}q.push(val);/*** @brief 通知所有线程 notify_all(),通知一个线程 notify_one()* 通知其他所有的线程,我生产了一个物品你们赶紧地去消费* 其他线程得到了该通知就会从等待状态 => 阻塞状态 => 获取互斥锁之后才能继续执行*/cv.notify_all();cout << "生产者 生产: " << val << "号物品\n";}void get() {unique_lock<std::mutex> lck(mtx);//!NOTE: 这里写成 while 是为了防止 cv.wait 被虚假唤醒while (q.empty()) {// 消费者线程发现队列为空就需要通知生产者线程先生产物品// #1 进入等待状态 #2 释放 mtxcv.wait(lck);}int val = q.front();q.pop();// 通知其他所有的线程,我消费了一个物品你们赶紧地去生产cv.notify_all();cout << "消费者 消费: " << val << "号物品\n";}private:queue<int> q;
};void producer(Queue *q)  // 生产者线程
{for (int i = 1; i <= 10; ++i) {q->put(i);std::this_thread::sleep_for(std::chrono::microseconds(100));}
}void consumer(Queue *q)  // 消费者线程
{for (int i = 1; i <= 10; ++i) {q->get();std::this_thread::sleep_for(std::chrono::microseconds(100));}
}int main() {Queue q;std::thread t1(producer, &q);std::thread t2(consumer, &q);t1.join();t2.join();return 0;
}

参考:

  • https://www.0xffffff.org/2016/02/11/38-c+±concurrency/
  • http://faq.0xffffff.org/question/2014/07/28/the-question-on-mutex-and-cond/

源码地址:链接

🔥 C++ 面试总结 CPPGuide 🔥

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/57007.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

保护隐私的第一步:从更新浏览器开始

当今社会已经进入了数字化和网络化的时代&#xff0c;而网络安全问题也日益突显。随着互联网在我们生活中的不断渗透&#xff0c;网络威胁变得愈发普遍和隐蔽。在这样的背景下&#xff0c;网络浏览器作为人们访问互联网的主要工具之一&#xff0c;不仅为我们提供了便捷的上网方…

【QT】ComboBox的使用(14)

ComboBox这个控件我常用于多文本的储存、调用&#xff0c;正如他的中文意思为&#xff1a;下拉列表框。 下拉列表框&#xff1a;字面意思就是一个多文本的列表框&#xff0c;今天来看下如何使用ComboBox这个控件。 一.环境配置 1.python 3.7.8 可直接进入官网下载安装&…

【Ubuntu】从Graylog到Grafana Loki:构建更强大的网络设备管理和监控系统

在将Graylog部署到生产环境时&#xff0c;我们遇到了一些问题&#xff0c;其中最主要的是无法安装MongoDB并且无法随时重启机器去修改BIOS设置来修复问题 【WARNING: MongoDB 5.0 requires a CPU with AVX support, and your current system does not appear to have that! 】。…

【论文阅读】HOLMES:通过关联可疑信息流进行实时 APT 检测(SP-2019)

HOLMES: Real-time APT Detection through Correlation of Suspicious Information Flows S&P-2019 伊利诺伊大学芝加哥分校、密歇根大学迪尔伯恩分校、石溪大学 Milajerdi S M, Gjomemo R, Eshete B, et al. Holmes: real-time apt detection through correlation of susp…

14-redis

一 Redis概述 1 为什么要用NoSQL 单机Mysql的美好年代 在90年代&#xff0c;一个网站的访问量一般都不大&#xff0c;用单个数据库完全可以 轻松应付。在那个时候&#xff0c;更多的都是静态网页&#xff0c;动态交互类型的网站不多。 遇到问题&#xff1a; 随着用户数的增长…

ffmpeg windows环境MinGW+msys2编译so库

一、安装MinGW 1.1、下载MinGW 1.2、下载完成后&#xff0c;会得到一个名为 mingw-get-setup.exe 的安装包&#xff0c;双击打开它&#xff0c;可以看到如下的对话框&#xff1a; 1.3、直接点击“Install”&#xff0c;进入下面的对话框 1.4、可根据自己操作系统的实际情况&am…

【学习FreeRTOS】第17章——FreeRTOS任务通知

1.任务通知的简介 任务通知&#xff1a;用来通知任务的&#xff0c;任务控制块中的结构体成员变量 ulNotifiedValue就是这个通知值。 使用队列、信号量、事件标志组时都需另外创建一个结构体&#xff0c;通过中间的结构体进行间接通信&#xff01; 使用任务通知时&#xff0c…

【C++】详解声明和定义

2023年8月28日&#xff0c;周一下午 研究了一个下午才彻底弄明白... 写到晚上才写完这篇博客。 目录 声明和定义的根本区别结构体的声明和定义声明结构体 定义结构体类的声明和定义函数的定义和声明声明函数 定义函数变量声明和定义声明变量定义变量 声明和定义的根本区别 …

RabbitMQ笔记-RabbitMQ基本术语

RabbitMQ基本术语 相关概念; 生产者&#xff08;Producer&#xff09;&#xff1a;投递消息。消息&#xff1a;消息体&#xff08;payload&#xff09;标签&#xff08;label&#xff09;&#xff1b;生产者把消息交给rabbitmq&#xff0c;rabbitmq会根据标签把消息发给感兴趣…

15-模型 - 一对多 多对多

一对多&#xff1a; 1. 在多的表里定义外键 db.ForeignKey(主键) 2. 增加字段 db.relationship 建立联系 ("关联表类名","反向引用名") from ext import db# 一 class User(db.Model):id db.Column(db.Integer, primary_keyTrue, autoincrementTrue)us…

LeetCode——回溯篇(一)

刷题顺序及思路来源于代码随想录&#xff0c;网站地址&#xff1a;https://programmercarl.com 目录 77. 组合 216. 组合总和 III 17. 电话号码的字母组合 39. 组合总和 40. 组合总和 II 77. 组合 给定两个整数 n 和 k&#xff0c;返回范围 [1, n] 中所有可能的 k 个数的…

4G智慧电力物联网:建设高效智能,引领电力行业革新!

随着4G与物联网技术的快速发展为电力行业提供了更高效、可靠、智能化的解决方案。本文中智联物联将为大家分享智慧电力系统中的一些关键的物联网技术和通讯设备&#xff0c;如工业4G路由器、分布式发电站、数据采集传输、远程监控管理以及变电站监测。 光伏发电站是电力行业中重…

万级数据优化EasyExcel+mybatis流式查询导出封装

文章目录 前言.千万级数据优化一. 直接上流式查询封装工具代码二. 传统分页导出查询三. 流式查询概念游标查询 前言.千万级数据优化 我们不妨先给大家讲一个概念&#xff0c;利用此概念我们正好给大家介绍一个数据库优化的小技巧&#xff1a; 需求如下&#xff1a;将一个地市表…

在kaggle中用GPU使用CGAN生成指定mnist手写数字

文章目录 1项目介绍2参考文章3代码的实现过程及对代码的详细解析独热编码定义生成器定义判别器打印我们的引导信息模型训练迭代过程中生成的图片损失函数的变化 4总结5 模型相关的文件 1项目介绍 在GAN的基础上进行有条件的引导生成图片cgan 2参考文章 GAN实战之Pytorch 使用…

Django基础6——数据模型关系

文章目录 一、基本了解二、一对一关系三、一对多关系3.1 增删改查3.2 案例&#xff1a;应用详情页3.2 案例&#xff1a;新建应用页 四、多对多关系4.1 增删改查4.2 案例&#xff1a;应用详情页4.3 案例&#xff1a;部署应用页 一、基本了解 常见数据模型关系&#xff1a; 一对一…

好用的网页制作工具就是这6个,快点来看!

对于网页设计师来说&#xff0c;好用的网页设计工具是非常重要的&#xff0c;今天本文收集了6个好用的网页设计工具供设计师自由挑选使用。在这6个好用的网页设计工具的帮助下&#xff0c;设计师将获得更高的工作效率和更精致的网页设计效果&#xff0c;接下来&#xff0c;就一…

【Java alibabahutool】JSON、Map、实体对象间的相互转换

首先要知道三者的互转关系&#xff0c;可以先将JSON理解成是String类型。这篇博文主要是记录阿里巴巴的JSONObject的两个方法。toJSONString()以及parseObject()方法。顺便巩固Map与实体对象的转换技巧。 引入依赖 <!-- 阿里巴巴 JSON转换 以下二选一即可 没有去细研究两者…

Centos 7.6 安装mongodb

以下是在CentOS 7.6上安装MongoDB的步骤&#xff1a; 打开终端并以root用户身份登录系统。 创建一个新的MongoDB存储库文件 /etc/yum.repos.d/mongodb-org-4.4.repo 并编辑它。 sudo vi /etc/yum.repos.d/mongodb-org-4.4.repo在编辑器中&#xff0c;添加下面的内容到文件中并…

什么是字体图标(Icon Font)?如何在网页中使用字体图标?

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 字体图标&#xff08;Icon Font&#xff09;⭐ 如何在网页中使用字体图标⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 记得点击上方或者右侧链接订阅本专栏哦 几何带你启航前端之旅 欢迎来到前端入门之旅&a…