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

embedded/2024/10/18 23:26:00/

文章目录

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


http://www.ppmy.cn/embedded/48756.html

相关文章

C语言 图形化界面方式连接MySQL【C/C++】【图形化界面组件分享】

博客主页&#xff1a;花果山~程序猿-CSDN博客 文章分栏&#xff1a;MySQL之旅_花果山~程序猿的博客-CSDN博客 关注我一起学习&#xff0c;一起进步&#xff0c;一起探索编程的无限可能吧&#xff01;让我们一起努力&#xff0c;一起成长&#xff01; 目录 一.配置开发环境 二…

SQL AND OR 运算符的使用与区别

SQL AND & OR 运算符的使用与区别 SQL(Structured Query Language)是一种用于管理关系数据库的编程语言。在SQL中,AND和OR运算符用于在WHERE子句中组合条件,以便更精确地筛选数据。本文将详细介绍SQL中的AND和OR运算符,包括它们的使用方法和区别。 1. SQL AND 运算符…

门面模式Api网关(SpringCloudGateway)

1. 前言 当前通过Eureka、Nacos解决了服务注册和服务发现问题&#xff0c;使用Spring Cloud LoadBalance解决了负载均衡的需求&#xff0c;同时借助OpenFeign实现了远程调用。然而&#xff0c;现有的微服务接口都直接对外暴露&#xff0c;容易被外部访问。为保障对外服务的安全…

WordPress模板推荐

WordPress外贸主题 wordpress跨境电商独立站主题&#xff0c;wordpress外贸建站模板。 手机配件wordpress外贸网站模板 充电器、移动电源、手机膜、手机电池、手机壳、手机转接头等手机配件wordpress外贸网站模板。 毛巾WordPress外贸主题 毛巾、面巾、婴童毛巾、浴巾、方巾、…

中介子方程十六

X$XFX$XEXyXαXiX$XαXiXrXkXtXyX$XpXVX$XdXuXWXtXWXuXdX$XVXpX$XyXtXkXrXiXαX$XiXαXyXEX$XFX$XEXyXαXiX$XαXiXrXkXtXyX$XpXVX$XdXuXWXtXWXuXdX$XVXpX$XyXtXkXrXiXαX$XiXαXyXEX$XαXηXtXαX$XWXyX$XyXWX$XpXαXqXηX$XeXαXhX$XdX$XpX$XdX$XyXeXαX$XEXyXαXiX$XαXiXrX…

板凳------56.Linux/Unix 系统编程手册(下) -- SOCKET 介绍

56.1.概述 socket 是一种IPC方法&#xff0c;允许位于同一主机或使用网络连接起来的不同主机上的应用程序之间交换数据。 UNIX 允许位于同一主机系统上的应用程序之间通信 Internet domain IPv4 and IPV6 // socket 通信方式 1.各个应用程序创建一个socket&#xff0c;socket是…

二叉树的实现(初阶数据结构)

1.二叉树的概念及结构 1.1 概念 一棵二叉树是结点的一个有限集合&#xff0c;该集合&#xff1a; 1.或者为空 2.由一个根结点加上两棵别称为左子树和右子树的二叉树组成 从上图可以看出&#xff1a; 1.二叉树不存在度大于2的结点 2.二叉树的子树有左右之分&#xff0c;次序不能…

旧物回收系统中多源数据融合的探索

内容概要&#xff1a; 在旧物回收系统的日常运营中&#xff0c;处理和分析来自多个渠道的数据对于优化回收流程、提高回收效率至关重要。本文将探讨旧物回收系统中涉及的多源数据融合&#xff0c;并分析其如何为系统带来更大的价值。 一、多源数据的来源与特点 旧物回收系统…