【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.rhkb.cn/news/109820.html

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

相关文章

基于Java+SpringBoot+Vue前后端分离社区医院管理系统设计和实现

博主介绍&#xff1a;✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专…

若依移动端Ruoyi-App 项目的后端项目入门

后端项目运行 运行报错 Error creating bean with name sysConfigServiceImpl: Invocation of init method failed 数据库创建了。 代码连接数据库地方了也匹配上了。但是还是报错。 分析 &#xff1a; 想起来我电脑从来没有安装过redis 下载安装redis到windows 链接&…

煤质软化:提升采矿效率的关键技术

煤质软化&#xff1a;提升采矿效率的关键技术 随着煤炭的广泛应用和能源需求的增长&#xff0c;对煤炭采矿和利用的要求也不断提高。传统的煤炭采矿方法往往面临一些困难和挑战&#xff0c;如厚层、高硬度以及强抗压性等问题&#xff0c;导致采掘效率低下、操作困难&#xff0c…

4.14 HTTPS 中 TLS 和 TCP 能同时握手吗?

目录 实现HTTPS中TLS和TCP同时握手的前提&#xff1a; 什么是TCP Fast Open&#xff1f; TLS v1.3 TCP Fast Open TLSv1.3 HTTPS都是基于TCP传输协议实现的&#xff0c;得先建立完可靠得TCP连接才能做TLS握手的事情。 实现HTTPS中TLS和TCP同时握手的前提&#xff1a; 1、…

9.4 集成功率放大电路

OTL、OCL 和 BTL 电路均有各种不同输出功率和不同电压增益的集成电路。应当注意&#xff0c;在使用 OTL 电路时&#xff0c;需外接输出电容。为了改善频率特性&#xff0c;减小非线性失真&#xff0c;很多电路内部还引入深度负反馈。这里以低频功放为例。 一、集成功率放大电路…

Unity编辑器扩展:提高效率与创造力的关键

Unity编辑器扩展&#xff1a;提高效率与创造力的关键 前言 一、理解Unity编辑器二、扩展Unity编辑器的意义三、扩展Unity编辑器的必要性四、Unity编辑器的扩展方式五、扩展Unity编辑器的步骤六、Unity编辑器扩展的应用案例七、总结 前言 Unity是一款广泛使用的游戏开发引擎&am…

机器学习十大算法之七——随机森林

0 引言 集成学习&#xff08;ensemble learning&#xff09;是时下非常流行的机器学习算法&#xff0c;它本身不是一个单独的机器学习算法&#xff0c;而是通过在数据上构建多个横型&#xff0c;集成所有模型的建模结果&#xff0c;基本上所有的机器学习领域都可以看到集成学习…

前端将file文件传给后台,后台将文件传给前台(包含上传下载)

前端将file文件传给后台&#xff0c;后台将文件传给前台&#xff08;包含上传下载&#xff09; 在开发过程中&#xff0c;经常会遇见对文件的处理。 例如&#xff1a;在上传、下载文件时&#xff0c;需要在前端选完文件传到后台传到服务器&#xff1b;或者文件从后台&#xf…

RTSP/Onvif协议安防视频平台EasyNVR录像模式自定义操作

TSINGSEE青犀视频安防监控平台EasyNVR可支持设备通过RTSP/Onvif流媒体协议接入&#xff0c;并能对接入的视频流进行处理与多端分发&#xff0c;包括RTSP、RTMP、HTTP-FLV、WS-FLV、HLS、WebRTC等多种格式。在智慧安防等视频监控场景中&#xff0c;EasyNVR可提供视频实时监控直播…

linux并发服务器 —— 动态库和静态库实战(一)

-E 预处理指定源文件 -S 编译指定源文件 -c 汇编指定源文件 -o 生成可执行文件 -I directory 指定Include包含文件的搜索目录 -g 编译的时候生成调试信息 -D 在程序编译时指定一个宏 -w 不生成任何的警告信息 -Wall 生成所有警告 -On n:0~3&#xff1b;表示编译器的优…

pip install bz2 和readline失败

python3.7.5 在跑模型时报错找不到bz2,使用pip install bz2 安装失败 bz2和readline应该是python自带的包 解决方案&#xff1a;重新编译安装python3.7.5,参考&#xff1a; https://www.hiascend.com/document/detail/zh/CANNCommunityEdition/70RC1alpha002/softwareinstall/…

Android工具条

在底层&#xff0c;所有通过主题得到应用条的活动都使用ActionBar类实现它的应用条。不过最新的应用条特性已经增加到AppCompat支持库中的Toolbar类。这意味着&#xff0c;如果你想在应用中使用最新的应用条特性&#xff0c;就需要使用支持库中的ToolBar类。 如何增加工具条 1…

❤ windows 安装后台java开发环境JDK 、MySQL 、Redis

❤ windows 安装后台java开发环境 1、windows 安装 JDK. 下载地址&#xff1a; http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html 1、下载安装 官网点击下载安装 网盘 jdk安装包 链接&#xff1a;https://pan.baidu.com/s/1sdxA6B…

Linux学习之DNS服务的原理

DNS服务一些理论 域名系统&#xff08;Domain Name System&#xff0c;DNS&#xff09;是互联网的核心应用服务&#xff0c;可以通过IP地址查询到域名&#xff0c;也可以通过域名查询到IP地址。 FQDN&#xff08;Full Qualified Domain Name&#xff09;是完全限定域名&#xf…

【ES6】—【新特性】—Symbol详情

一、一种新的原始数据类型 定义&#xff1a;独一无二的字符串 二、 声明方式 1. 无描述声明 let s1 Symbol() let s2 Symbol() console.log(s1, s2) // Symbol() Symbol() console.log(s1 s2) // falsePS: Symbol 声明的值是独一无二的 2. 有描述的声明 let s1 Symb…

【如何对公司网络进行限速?一个案例详解】

有不少朋友问到了关于企业网络QoS配置&#xff0c;这个确实在实际网络应用中非常多&#xff0c;基本上大部分企业或个人都用到这个功能&#xff0c;本期我们详细了解下QoS如何对宽带进行限制&#xff0c;QoS如何企业中应用。 一、什么是QoS? Qos是用来解决网络延迟和阻塞等问…

【微信红包】Axure聊天发红包原型图,含流程图和PRD产品文档

作品概况 页面数量&#xff1a;共 60 页 兼容软件&#xff1a;Axure RP 9/10&#xff0c;不支持低版本 应用领域&#xff1a;聊天软件、社交软件 作品申明&#xff1a;页面内容仅用于功能演示&#xff0c;无实际功能 作品特色 本作品为「发红包」的原型设计图&#xff0c…

为什么网络互联地址设置为30位地址

对于点对点链路&#xff0c;为了节约IPv4地址&#xff0c;一般为其分配/30地址块&#xff0c;这样包含4个地址&#xff1a;最小地址作为网络地址&#xff0c;最大地址作为广播地址&#xff0c;剩余两个可分配地址&#xff0c;分配给链路两端的接口&#xff0c;这是最普遍的方法…

《网络是怎样连接的》(五)

本文主要取材于 《网络是怎样连接的》 第五章。 目录 5.1 Web服务器的部署地点 5.2 防火墙的结构和原理 5.3服务器负载平衡 5.4 使用缓存服务器分担负载 5.5 内容分发服务 简述&#xff1a;本文主要内容是解释 网络包如何朝服务器前进&#xff0c;并通过服务器前面的防…

php_webshell免杀--从0改造你的AntSword

0x00 前言&#xff1a; 为什么会有改造蚁剑的想法&#xff0c;之前看到有做冰蝎的流量加密&#xff0c;来看到绕过waf&#xff0c;改造一些弱特征&#xff0c;通过流量转换&#xff0c;跳过密钥交互。 但是&#xff0c;冰蝎需要反编译去改造源码&#xff0c;再进行修复bug&am…