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

news2024/10/6 0:29:28

文章目录

  • 一、模式简介
  • 二、头文件、全局变量
  • 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++多线程:生产者消费者模式
如果你觉得文章不错,对你有帮助,不妨点个关注,欢迎批评指正,谢谢!

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

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

相关文章

Github 2024-06-14 开源项目日报Top10

根据Github Trendings的统计,今日(2024-06-14统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量JavaScript项目2Python项目2非开发语言项目2TypeScript项目1Dart项目1Rust项目1Lua项目1Java项目1Jupyter Notebook项目1从零开始构建你喜爱的技…

Jira,一个强大灵活的项目和任务管理工具 Python 库

目录 01初识 Jira 为什么选择 Jira? 02安装与配置 安装 jira 库 配置 Jira 访问 获取 API token: 配置 Python 环境: 03基本操作 创建项目 创建任务 查询任务 更新任务 删除任务 04高级操作 处理子任务 搜索任务 添加附件 评论任务 05实战案例 自动化创建…

java:spring actuator扩展原有info endpoint的功能

# 项目代码资源&#xff1a; 可能还在审核中&#xff0c;请等待。。。 https://download.csdn.net/download/chenhz2284/89437506 # 项目代码 【pom.xml】 <dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId&…

DomoAI让你轻松变身视频达人!支持20s完整视频生成!

账号注册 官网&#xff1a;https://www.domoai.app/zh-Hant/library 功能 支持不同风格的视频类型&#xff0c;支持图片转视频&#xff0c;支持文字转图片&#xff0c;支持静态图片变为动态。 可以切换语言为中文 风格转换 选择不同风格的 支持生成20s&#xff0c;目前接触…

牛客网金九银十最新版互联网Java高级工程师面试八股文出炉!面面俱到,太全了

前言 作为一个 Java 程序员&#xff0c;你平时总是陷在业务开发里&#xff0c;每天噼里啪啦忙敲着代码&#xff0c;上到系统开发&#xff0c;下到 Bug 修改&#xff0c;你感觉自己无所不能。然而偶尔的一次聚会&#xff0c;你听说和自己一起出道的同学早已经年薪 50 万&#x…

IDEA创建web项目

IDEA创建web项目 第一步&#xff1a;创建一个空项目 第二步&#xff1a;在刚刚创建的项目下创建一个子模块 第三步&#xff1a;在子模块中引入web 创建结果如下&#xff1a; 这里我们需要把这个目录移到main目录下&#xff0c;并改名为webapp&#xff0c;结果如下 将pom文件…

贪心算法学习四

例题一 解法&#xff08;暴⼒解法 -> 贪⼼&#xff09;&#xff1a; 暴⼒解法&#xff1a; a. 依次枚举所有的起点&#xff1b; b. 从起点开始&#xff0c;模拟⼀遍加油的流程 贪⼼优化&#xff1a; 我们发现&#xff0c;当从 i 位置出发&#xff0c;⾛了 step 步…

如何在 Windows 上安装 MySQL(保姆级教程2024版)

MySQL 是最流行的数据库管理系统 (DBMS) 之一。它轻量、开源且易于安装和使用&#xff0c;因此对于那些刚开始学习和使用关系数据库的人来说是一个不错的选择。 本文主要系统介绍Windows的环境下MySQL的安装过程和验证过程。 目录 1 安装过程 1.1 前置要求 1.2 下载并安装 …

【three.js】旋转、缩放、平移几何体

目录 一、缩放 二、平移 三、旋转 四、居中 附源码 BufferGeometry通过.scale()、.translate()、.rotateX()、.rotateY()等方法可以对几何体本身进行缩放、平移、旋转,这些方法本质上都是改变几何体的顶点数据。 我们先创建一个平面物体,样子是这样的。 一、缩放 // 几何…

基于Matlab的人脸表情识别系统(GUI界面)【W5】

简介&#xff1a; 该系统是一个基于Matlab开发的人脸表情识别应用程序&#xff0c;旨在识别输入图像中的人脸表情&#xff0c;并通过直观的图形用户界面&#xff08;GUI&#xff09;向用户展示识别结果。系统结合了图像处理、机器学习和用户交互技术&#xff0c;使用户能够轻松…

【PL理论】(24) C- 语言:有块的作用域 | 更新的语法 | 新的语义域 | 环境 vs. 内存

&#x1f4ad; 写在前面&#xff1a;我们将再次扩展之前的C语言&#xff0c;让我们向这种语言引入“作用域”的概念。 目录 0x00 C- 语言&#xff1a;有块的作用域 0x01 C- 语言&#xff1a;更新的语法 0x02 新的语义域 0x03 环境 vs. 内存 0x00 C- 语言&#xff1a;有块的…

DistilBertModel模型的简单解释

前言 DistilBertModel((embeddings): Embeddings((word\_embeddings): Embedding(30522, 768, padding\_idx0)(position\_embeddings): Embedding(512, 768)(LayerNorm): LayerNorm((768,), eps1e-12, elementwise\_affineTrue)(dropout): Dropout(p\0.1, inplaceFalse))(trans…

洗地机哪个牌子质量好,性价比高?一文盘点市场热门选择

近年来&#xff0c;洗地机因为其能快速的解决我们耗时、费力又繁琐的地板清洁工作&#xff0c;备受人们的喜爱。但面对多款设备不同功能和特点相近的洗地机&#xff0c;你可能会疑惑&#xff1a;“洗地机哪个牌子质量好&#xff1f;”&#xff0c;如果你正在寻找一款高效、便捷…

视频剪辑可以赚钱吗 快速学会视频剪辑的方法

由于视频剪辑的需求不断增长&#xff0c;学会视频剪辑成为一项自媒体必备的技能&#xff0c;这个技能可以为个人带来收入和职业发展带来机会。无论是作为自由职业者还是在公司工作&#xff0c;掌握视频剪辑技能都可以为你提供更多的工作机会和竞争优势。这篇文章将讲解视频剪辑…

mongodb-java apispringboot整合mongodb

mongodb入门mongodb-java api的使用springboot整合mongodb评论 一 MongoDB 1.1 MongoDB简介 ​ MongoDB是一个基于分布式文件存储的数据库。由C语言编写。旨在为WEB应用提供可扩展的高性能数据存储解决方案。 ​ MongoDB是一个介于关系数据库和非关系数据库之间的产品&…

Java——可变参数

一、可变参数 1、介绍 Java的可变参数&#xff08;Varargs&#xff09;是一种语法特性&#xff0c;允许一个方法接受不定数量的参数。可变参数的使用通过在参数类型后面添加省略号&#xff08;...&#xff09;实现。这使得方法在调用时可以传入不同数量的参数&#xff0c;而不…

【靶场搭建】-01- 在kali上搭建DVWA靶机

1.DVWA靶机 DVWA&#xff08;Damn Vulnerable Web Application&#xff09;是使用PHPMysql编写的web安全测试框架&#xff0c;主要用于安全人员在一个合法的环境中测试技能和工具。 2.下载DVWA 从GitHub上将DVWA的源码clone到kali上 git clone https://github.com/digininj…

新机器到底是入手DDR5还是DDR4?2024年的DDR5能否战未来?

前言 前几天有个小伙伴发来问候&#xff1a;在今年&#xff08;2024年&#xff09;装新机器到底是选择DDR4还是DDR5&#xff1f; 小白淡定了抿了一口茶&#xff0c;缓缓道&#xff1a;…… 小伙伴不淡定了&#xff01;上来就是一句&#xff1a;2024年的DDR5能否战未来&#…

用一个ESP32S3-Zero把有线键盘变为无线

三脚猫最近一直琢磨&#xff0c;那些喜欢买剪线键盘&#xff0c;以及自制键盘瞎折腾的人都是怎么搞的。经过不懈努力&#xff0c;终于想明白除了直接的硬件一个个pin针的高低电压判断后转给蓝牙&#xff0c;拿到现成的古董剪线键盘还有一个方式其实是在usb host转发给蓝牙类似这…

凭什么只有我不能触摸她的心?JavaScript的作用域?作用域链?预编译?

前言 凭什么后来者居上&#xff1f;凭什么她的心只有我不能触摸&#xff1f;&#xff08;o(╥﹏╥)o&#xff09; 让我从JavaScript中的作用域、作用域链和预编译开始聊聊&#xff0c;相信你一定能从中有收获的 什么是的作用域&#xff1f; 简单来说就是能被访问的区域&…