C++多线程:生产者消费者模式

文章目录

  • 一、模式简介
  • 二、头文件、全局变量
  • 2.1 仓库类的设计
    • 2.1.1 关于仓库类的分析
    • 2.1.2 仓库类的设计代码
  • 2.2 工厂类的设计
    • 2.2.1 关于工厂类的分析
    • 2.2.2 工厂类的设计代码
      • a 将产品item放到仓库repo
      • b 将产品item从仓库repo取出
      • c 生产者操作
      • d 消费者操作
    • 2.2.3 主函数代码
  • 三、运行效果和说明

一、模式简介

假设你有一个工厂Factory,配有一个仓库Repository,仓库大小为20,要生产200个产品,你有两个工人producer,三个受众群体consumer,应当如何描述这200个产品从生产到消费的全过程?
这一种生活中的问题在多线程程序设计上称为生产者消费者模式,形象的表示了多线程中数据获取、数据存储、数据处理的过程,关键点在于用互斥锁、条件变量等解决数据存取、同时存、同时取之间的冲突。

二、头文件、全局变量

引入头文件,按照问题描述,可以得到计划产品个数和仓库大小两个全局变量。

#include<iostream>
#include<thread>
#include<mutex>
#include<condition_variable>
#include<queue>
using namespace std;

const int kProduceAimSize = 200;	//计划产品个数
const int kRepositorySize = 20;		//仓库大小

2.1 仓库类的设计

2.1.1 关于仓库类的分析

按照上上面提出的问题,我们大致能分析得到以下线索:
工厂描述了问题总体,是一个用于解决问题的类,核心在于用于数据交互的仓库类。
对于仓库的数据管理,应当设计三个互斥量,依次为:存取冲突互斥量、同时存的互斥量、同时取的互斥量。两个条件变量,依次表示为:仓库状态为空、仓库状态为满。

2.1.2 仓库类的设计代码

template <typename _T>
class Repository {		//仓库
public:
	deque<_T> items_buff;
	mutex mmutex;					//生产者和消费者互斥量,解决从仓库中存取的冲突
	mutex mmutex_produce;			//生产者之间的互斥量,解决同时生产同一个产品的冲突
	mutex mmutex_consume;			//消费者之间的互斥量,解决同时消费同一个产品的冲突
	mutex mmutex_print;				//保证由cout打印的提示信息不会中断

	condition_variable repo_full;	//描述仓库为满的条件变量
	condition_variable repo_empty;	//描述仓库为空的条件变量

	size_t cnt_produce;				//生产者目前产生的产品总数
	size_t cnt_consume;				//消费者目前消费的产品总数
	size_t current_size;			//仓库中产品个数
	Repository() {					//初始化
		cnt_produce=0;
		cnt_consume=0;
		current_size = 0;
	}
};

2.2 工厂类的设计

2.2.1 关于工厂类的分析

按照问题描述,工厂由工人和消费者组成,流程上需要完成四个基本任务:
1、生产产品
2、将产品放入仓库
3、从仓库中取出产品
4、消费产品
同时注意工厂类包含一个仓库类实例。

因此工厂类的大致架构如下:
在这里插入图片描述

2.2.2 工厂类的设计代码

a 将产品item放到仓库repo

void PutInto(Repository<_T>& repo, _T item) {
	unique_lock< mutex> lk(repo.mmutex);
	//仓库是一个存取数据的地方,因而对仓库进行操作需要上锁。(解决从仓库中存取的冲突)

	while (repo.current_size == kRepositorySize) {	//如果仓库为满,无法向仓库中继续放
		unique_lock<mutex> pt(repo.mmutex_print);
		cout << "仓库已满,无法放入更多产品,需先消费。" << endl;
		pt.unlock();
		repo.repo_full.wait(lk);	//将取互斥量抛出,等待其他线程通知
	}

	repo.items_buff.push_back(item);
	repo.current_size++;
	repo.repo_empty.notify_all();	//有产品了,唤醒仓库为空情况下的线程
}

b 将产品item从仓库repo取出

_T GetFrom(Repository<_T>& repo) {
	unique_lock<mutex> lk(repo.mmutex);	
	//仓库是一个存取数据的地方,因而对仓库进行操作需要上锁。(解决从仓库中存取的冲突)

	while (repo.current_size == 0) {	//如果仓库为空,等待
		unique_lock<mutex> pt(repo.mmutex_print);
		cout << "无货源,等待..." << endl;
		pt.unlock();
		repo.repo_empty.wait(lk);	//将存取互斥量抛出,等待其他线程通知
	}

	_T data = repo.items_buff.front();
	repo.items_buff.pop_front();
	repo.current_size--;
	repo.repo_full.notify_all();	//从仓库取出了产品,唤醒仓库为满情况下的线程
	return data;
}

c 生产者操作

void ProduceTask() {
 	bool ready_to_exit = false; //线程结束条件
	while (true) {
		unique_lock<mutex> lk(repo.mmutex_produce);	//不能生产同一个产品
		
		if (repo.cnt_produce < kProduceAimSize) {//需要生产(没达到目标)
			repo.cnt_produce++;
			//生产产品假设0.05s
			this_thread::sleep_for(0.05s);	
			_T item = repo.cnt_produce;	//生产产品具体过程的化简
			
			unique_lock<mutex> pt(repo.mmutex_print);
			cout << "生产者id:" << this_thread::get_id() << " as[" << item<<"]" << endl;
			pt.unlock();
			
			PutInto(repo, item);
		}
		else {
			ready_to_exit = true;
		}
		if (ready_to_exit == true)break;
	}
}

d 消费者操作

void ConsumeTask() {
	bool ready_to_exit = false;	//线程结束条件
	while (true){
		unique_lock<mutex> lk(repo.mmutex_consume);	//不能同时消费一个产品
	
		if (repo.cnt_consume < kProduceAimSize) {//需要消费(没达到目标)
			_T item = GetFrom(repo);	//获取需要消费的产品
			repo.cnt_consume++;
			//消费产品代码,假设消费0.06s
			this_thread::sleep_for(0.06s);	//消费过程的化简
			
			unique_lock<mutex> pt(repo.mmutex_print);
			cout << "消费者id:" << this_thread::get_id() << " as[" << item<<"]" << endl;
			pt.unlock();
		}
		else {
			ready_to_exit = true;
		}
		if (ready_to_exit == true)break;
	}
}

2.2.3 主函数代码

int main() {
	cout << "主线程id:" << this_thread::get_id() << endl;
	//一个工厂类
	Factory<int> MyFactory;
	
	//两个生产者
	thread producer1(&Factory<int>::ProduceTask, ref(MyFactory));
	thread producer2(&Factory<int>::ProduceTask, ref(MyFactory));
	
	//三个消费者
	thread consumer1(&Factory<int>::ConsumeTask, ref(MyFactory));
	thread consumer2(&Factory<int>::ConsumeTask, ref(MyFactory));
	thread consumer3(&Factory<int>::ConsumeTask, ref(MyFactory));

	producer1.join();	
	producer2.join();

	consumer1.join();
	consumer2.join();
	consumer3.join();
	return 0;
}

三、运行效果和说明

有概率出现无货源、仓库满两种情况,均能进行正常处理,对于同一次运行,生产者id有两种,消费者id有三种,下面两张截图来自于不同运行。
C++多线程:生产者消费者模式
C++多线程:生产者消费者模式
如果你觉得文章不错,对你有帮助,不妨点个关注,欢迎批评指正,谢谢!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

LVS_Director + KeepAlived + 邮件报警

目录 一. 环境准备 二. 对master和backup操作 三. 配置master主机 四. 配置backup主机 六. 验证虚拟IP 七. 配置后端两个web服务器 对web1和web2主机都进行如下操作&#xff1a; 单独修改web1主机 单独修改web2主机 验证 八. 设置邮件报警 一. 环境准备 KeepAlive…

STM32单片机选型方法

一.STM32单片机选型方法 1.首先要确定需求&#xff1a; 性能需求&#xff1a;根据应用的复杂度和性能要求&#xff0c;选择合适的CPU性能和主频。 内存需求&#xff1a;确定所需的内存大小&#xff0c;包括RAM和Flash存储空间。 外设需求&#xff1a;根据应用所需的功能&…

六大维度全面焕新升级!麒麟信安服务器操作系统V3.6.1引领未来计算

昨日&#xff0c;openEuler 24.03 LTS 正式发布&#xff0c;麒麟信安作为openEuler社区重要贡献者和参与者&#xff0c;充分发挥自身在国产操作系统领域的技术优势&#xff0c;在打造安全可靠、极致体验的操作系统上与社区共同努力&#xff0c;同步推出服务器操作系统V3.6.1&am…

misc刷题记录(1)陇剑杯

[陇剑杯 2021]签到 题目内容&#xff1a;此时正在进行的可能是__________协议的网络攻击。&#xff08;如有字母请全部使用小写&#xff0c;填写样例&#xff1a;http、dns、ftp&#xff09;。得到的flag请使用NSSCTF{}格式提交。 打开统计&#xff0c;找到协议分级&#xff…

二层弹出框,点掉小弹出框后,遮罩层没有消失

解决办法把 父元素的vue实例对象的&#xff0c;最后一个元素删除。删除的就是遮罩层元素 thus.$ refs.dialig.$ parent.$ el.lastChild. remove()

dbForge Studioor MySQL v6 解锁版 安装教程(MYSQL数据库客户端)

前言 dbForge Studioor MySQL是一个在Windows平台被广泛使用的MySQL客户端&#xff0c;它能够使MySQL开发人员和管理人员在一个方便的环境中与他人一起完成创建和执行查询&#xff0c;开发和调试MySQL程序&#xff0c;自动化管理MySQL数据库对象等工作。 一、下载地址 下载链…

香橙派AIpro测试SPI通信

香橙派AIpro开发板上有一个SPI接口&#xff0c;如下图红框所示&#xff0c; 系统启动后&#xff0c;其对应的设备是 /dev/spidev0.0 一 硬件回环测试 香橙派AIpro上的系统自带spidev_test工具&#xff0c;非常方便&#xff0c;可以查看其帮助信息&#xff0c;如下&#xff0c…

探索AIGC与3D技术的融合:从图像到可探索的3D动态场景

随着人工智能和计算机图形技术的飞速发展,AIGC(人工智能生成内容)与3D技术的结合正在为我们打开一扇全新的创意之门。最近,我深入研究了几个令人兴奋的AIGC+3D方案,它们不仅展示了从单张图片或文本提示生成3D点云的强大能力,还进一步实现了AI虚拟试穿和生成高保真3D数字人…

金融与大模型:引领行业未来的创新融合

前言 在数字化浪潮席卷全球的今天&#xff0c;金融与大模型的结合正成为行业发展的新引擎。这种融合不仅为金融机构带来了前所未有的效率和准确性&#xff0c;也为金融市场的稳定与发展注入了新的活力。本文将基于当前的市场现状&#xff0c;结合金融环境的发展&#xff0c;深…

【docker实战】使用代理的坑

在docker公共仓库被封禁的日子里&#xff0c;大多数人更喜欢使用镜像仓库代理源。 网上教程一大把&#xff0c;似乎不使用代理&#xff0c;就不会使用docker一样。 上图就是我设置的代理源镜像仓库。通常是设置/etc/docker/daemon.json这个文件实现的。 这样设置之后&#xff0…

【MySQL】索引(上)

https://www.wolai.com/curry00/fzTPy3kSsMDEgEcdvo4G5w https://www.bilibili.com/video/BV1Kr4y1i7ru/?p69 https://jimhackking.github.io/%E8%BF%90%E7%BB%B4/MySQL%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/#%E7%B4%A2%E5%BC%95 索引是一种用于快速查询和检索数据的数据结构…

echarts写某个市地图

const geoJSON {"type":"FeatureCollection","features":[{"type":"Feature","properties":{"adcode":440303,"name":"罗湖区","center":[114.123885,22.555341],"…

照明灯具哪个品牌好,一文详细带你了解照明灯具种类有哪些

在孩子学习过程中&#xff0c;有一样物品的重要性不容忽视&#xff0c;那就是一盏提供舒适光源的照明灯具。那么照明灯具哪个品牌好&#xff1f;面对不断增加的学业负担&#xff0c;孩子们经常需要在夜晚借助台灯的光亮进行学习&#xff0c;这已经成为了家庭生活中普遍的情景。…

【全开源】多功能投票小程序源码(Uniapp+ThinkPHP+FastAdmin)

&#x1f4a5;**【热门推荐】多功能投票小程序&#xff0c;一键解决你的选择难题&#xff01;**&#x1f4a5; 基于ThinkPHPFastAdminUniapp开发的多功能系统&#xff0c;支持图文投票、自定义选手报名内容、自定义主题色、礼物功能(高级授权)、弹幕功能(高级授权)、会员发布、…

FullCalendar日历组件集成实战(15)

背景 有一些应用系统或应用功能&#xff0c;如日程管理、任务管理需要使用到日历组件。虽然Element Plus也提供了日历组件&#xff0c;但功能比较简单&#xff0c;用来做数据展现勉强可用。但如果需要进行复杂的数据展示&#xff0c;以及互动操作如通过点击添加事件&#xff0…

面向对象编程垃圾回收机制

系列文章目录 文章目录 系列文章目录前言一、垃圾回收机制&#xff08;Garbage Collection&#xff09; 前言 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站&#xff0c;这篇文章男女通用…

在AWS上运行的EKS Elastic Kubernetes Service 创建集群Cluster,Node group, Nodes

1. 前提条件 AWS Account: https://aws.amazon.com/free/Installing KubeCtl CLI https://docs.aws.amazon.com/eks/latest/userguide/getting-started-eksctl.htmlEKS Cluster RoleIAM Role for Node GroupVPCEC2 Key Pair which can be used to SSH to the worker nodesAWS …

植物ATAC-seq文献集锦(三)——果实发育篇

ATAC-seq在植物研究领域的应用我们已经介绍2期了&#xff0c;本期我们聚焦ATAC-seq技术在果实发育方向的应用案例。 植物ATAC-seq文献集锦&#xff08;一&#xff09;——基因组篇 植物ATAC-seq文献集锦&#xff08;二&#xff09;——生长发育篇 文献一&#xff1a;Ident…

论文阅读笔记:Cross-Image Relational Knowledge Distillation for Semantic Segmentation

论文阅读笔记&#xff1a;Cross-Image Relational Knowledge Distillation for Semantic Segmentation 1 背景2 创新点3 方法4 模块4.1 预备知识4.2 跨图像关系知识蒸馏4.3 Memory-based像素到像素蒸馏4.4 Memory-based像素到区域蒸馏4.5 整体框架 5 效果 论文&#xff1a;http…

Visual Studio Code远程linux计算云

一、前置条件 本机安装Visual Studio Code 打开Vscode时建议使用管理员权限打开&#xff0c;在这之前遇到了一些报错。 二、开始远程连接计算云 安装插件remote-ssh 2.点击远程资源管理器&#xff0c;之后在SSH这行的右侧&#xff0c;点击“”号&#xff0c;去新建远程 3.在窗…