c++实现生产者消费者的供需关系

news2024/9/21 4:23:53

一、生产者&消费者模式

生产者-消费者模式(Producer-Consumer Pattern)是一种常见的并发设计模式,这种模式最常见,所以把它单独拿出来,这种模式用于处理生产者和消费者之间的协调问题。生产者和消费者之间不直接关联或依赖,而是用一个第三方来协调双方的供需关系。这种模式解决了生产者生成数据和消费者处理数据之间的同步与缓冲问题。特别适合于需要协调多个线程生产和消费数据的场景。

二、生产者&消费者的设计案例

代码说明:通过对象池存放生产者产生的产品。即生产者生产的产品存入对象池中,消费者从对象池获取产品,生产者和消费者通过供需关系时间比例调节平衡。

完整代码

producer_consumer.cpp

#include <iostream>
#include <vector>
#include <mutex>
#include <condition_variable>
#include <memory>
#include <thread>
#include <queue>
#include <map>
#include <unordered_set>
#include <atomic>
#include <shared_mutex>
#include <algorithm>
#include <chrono>
#include <atomic>

std::mutex productMutex;

// 对象池
template <typename T>
class ObjectPool {
public:
    ObjectPool() : createdCount_(0) {}
    
    ~ObjectPool() {
        std::lock_guard<std::mutex> lock(mutex_);
        pool_.clear();
        creationTimes_.clear();
        destructionTimes_.clear();
    }
    
    std::shared_ptr<T> acquireObject() {
        std::unique_lock<std::mutex> lock(mutex_);
        cv_.wait(lock, [this]() { return !pool_.empty(); });
        auto obj = pool_.back().second;
        pool_.pop_back();
        return obj;
    }

    void addObject(std::shared_ptr<T> obj, int PID) {
        std::lock_guard<std::mutex> lock(mutex_);
        pool_.emplace_back(PID, obj);
        ++createdCount_;
        creationTimes_.insert({PID, std::make_pair(obj, std::chrono::system_clock::now())});
        cv_.notify_one();
    }

    void destructObject(std::shared_ptr<T> obj, int PID) {
        std::lock_guard<std::mutex> lock(mutex_);
        destructionTimes_.insert({PID, std::make_pair(obj, std::chrono::system_clock::now())});
    }

    void printStatus() {
        std::lock_guard<std::mutex> lock(mutex_);
        std::cout << "对象池总创建产品数: " << createdCount_ << std::endl;
        std::cout << "对象池中剩余产品数: " << pool_.size() << std::endl;
        for (const auto& [PID, obj] : pool_) {
        std::cout << "剩余产品PID: " << PID << std::endl;
        }

	std::cout << "\n*********** 生产者生产的所有产品信息 ************\n" << std::endl;
        for (const auto& [PID, pair] : creationTimes_) {
            std::time_t t = std::chrono::system_clock::to_time_t(pair.second);
            std::cout << "PID:" << PID << " 产品创建时间: " << std::ctime(&t);
        }

	std::cout << "\n------------ 消费者消耗的所有产品信息 ------------\n" << std::endl;
        for (const auto& [PID, pair] : destructionTimes_) {
            std::time_t t = std::chrono::system_clock::to_time_t(pair.second);
            std::cout << "产品" << PID << " 被买走时间: " << std::ctime(&t);
        }
    }

private:
    std::vector<std::pair<int, std::shared_ptr<T>>> pool_;
    std::mutex mutex_;
    std::condition_variable cv_;
    size_t createdCount_;
    std::multimap<int, std::pair<std::shared_ptr<T>, std::chrono::system_clock::time_point>> creationTimes_;
    std::multimap<int, std::pair<std::shared_ptr<T>, std::chrono::system_clock::time_point>> destructionTimes_;
};

// 产品
class Product {
public:
    Product() : data_(0) {}
    void setData(int data) { data_ = data; }
    int getData() const { return data_; }

private:
    int data_;
};

// 生产者
class Producer {
public:
    Producer(ObjectPool<Product>& pool, std::atomic<bool>& running,const int PRODUCETIME) : pool_(pool), running_(running), PRODUCETIME_(PRODUCETIME) {}
    ~Producer() {
    	generated_.clear();
    }
    
    void produce() {
        while (running_) {
            // 生产产品
            auto product = std::make_shared<Product>();
            if (generated_.size() < capacity_) {
            int data = std::rand() % capacity_;
            if (generated_.find(data) == generated_.end()) {
            generated_.insert(data);
            product->setData(data);
            std::this_thread::sleep_for(std::chrono::milliseconds(PRODUCETIME_));
            {
            std::lock_guard<std::mutex> lock(productMutex);
            std::cout << "已生产数字币: " << product->getData() << std::endl;
            }
            // 将产品放入池中
            pool_.addObject(product, product->getData());
            }
            } else {
            	std::cerr << "Error: 已超过最大产能负荷,生产者崩溃!" << std::endl;
            	return ;
            } 
        }
    }

private:
    ObjectPool<Product>& pool_;
    std::atomic<bool>& running_;
    int PRODUCETIME_;
    std::unordered_set<int> generated_; 
    const int capacity_ = 10000; // 焊四的最大产能
};

// 消费者
class Consumer {
public:
    Consumer(ObjectPool<Product>& pool, std::atomic<bool>& running, const int CONSUMETIME) : pool_(pool), running_(running), CONSUMETIME_(CONSUMETIME) {}

    void consume() {
        while (running_) {
            auto product = pool_.acquireObject();
            
            // 消费产品
            {
            std::lock_guard<std::mutex> lock(productMutex);
            std::cout << "已真金白银购买数字币: " << product->getData() << std::endl;
            }
            std::this_thread::sleep_for(std::chrono::milliseconds(CONSUMETIME_));
            // 将产品消耗掉
            pool_.destructObject(product, product->getData());
        }
    }

private:
    ObjectPool<Product>& pool_;
    std::atomic<bool>& running_;
    int CONSUMETIME_;
};

int main(int argc, char* argv[]) {

int PRODUCETIME = 100;	// default produce time
int CONSUMETIME = 100;	// default consume time

if (argc > 1) {
	PRODUCETIME = std::atoi(argv[1]);
}
if (argc > 2) {
	CONSUMETIME = std::atoi(argv[2]);
}
    // 创建对象池
    ObjectPool<Product> pool;
    std::atomic<bool> running(true);
    // 创建生产者和消费者
    Producer producer(pool, running, PRODUCETIME);
    Consumer consumer(pool, running, CONSUMETIME);

    // 启动生产者和消费者线程
    std::thread producerThread(&Producer::produce, &producer);
    std::thread consumerThread(&Consumer::consume, &consumer);

    // 模拟1分钟的产品生产和交易行为
    std::this_thread::sleep_for(std::chrono::minutes(1));
    running = false;
    
    // 数据缓冲
    std::this_thread::sleep_for(std::chrono::seconds(1));
    std::cout << "\n一分钟已到,正在清理资源! 即将停止程序运行 " << std::endl;
    
    
    // 查询对象池
    pool.printStatus();

    // 停止生产和交易行为
    producerThread.join();
    consumerThread.join();
    
    std::cout << "\n程序退出 " << std::endl;

    return 0;
}

demo演示

供给等于需求

设置了16秒的限时,默认供需时间关系比例为 1:1,demo如下

代码执行方式:

        g++ producer_consumer.cpp

        ./a.out

对象池打印结果:

        对象池总创建产品数: 159
        对象池中剩余产品数: 1
        剩余产品PID: 2379

供给小于需求

设置了16秒的限时,设置供需时间关系比例为 4:1,demo如下

代码执行方式:

        g++ producer_consumer.cpp

        ./a.out 400 100

对象池打印结果:

        对象池总创建产品数: 40
        对象池中剩余产品数: 0

供给大于需求

 设置了16秒的限时,设置供需时间关系比例为 1:4,demo如下

代码执行方式:

        g++ producer_consumer.cpp

        ./a.out 100 400

对象池打印结果:

        对象池总创建产品数: 159
        对象池中剩余产品数: 119
剩余产品PID: 886
剩余产品PID: 2777
剩余产品PID: 7793
剩余产品PID: 8335
剩余产品PID: 5386
剩余产品PID: 6649
剩余产品PID: 1421
剩余产品PID: 2362
......

三、总结

从上面的演示中可知,当供给等于需求,即生产&消费时间比例 = 1:1,生产者生产产品速度和消费者消费产品的速度基本保持一致,达到供需平衡。当供给小于需求,即生产&消费时间比例 = 4:1,输出信息明显放慢,说明生产者生产产品速度放慢,导致供给紧张,需求旺盛。当供给大于需求,即生产&消费时间比例 = 1:4,生产者生产产品速度过快,导致产能过剩。

预测:如果不限时,当供给小于需求,运行效果中,产出始终慢(效率低下),当供给大于需求,运行效果中,产品越堆越多,产能过剩(内存很可能不足!)。

通过c++代码可以模拟供需关系,有趣也有成就感。

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

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

相关文章

leveldb源码解析(一)——编解码

leveldb中&#xff0c;数字的存储统一采用小端序&#xff0c;通过对数字编码和压缩&#xff0c;节省了存储空间。 变长编码 小端序中&#xff0c;每个字节的最低位存储状态&#xff0c;其余7位存储数据。 status状态值说明&#xff1a; 1&#xff1a;该字节不是当前数字最后…

《Few-shot Object Counting and Detection》CVPR2022

概述 摘要&#xff1a; 论文提出了一个新的任务——少量样本目标计数和检测&#xff08;Few-shot Object Counting and Detection, FSCD&#xff09;。在这项任务中&#xff0c;研究者们旨在通过给定少量目标类别的示例边界框来计数和检测图像中所有目标对象。这项任务与少量样…

Your connection to this site is not secure

chrome 打开某一个网站的网页地址栏提示Your connection to this site is not secure,同一个网站的其它地址栏打开不会 无效的方案 浏览器地址栏输入: chrome://flags 找到下边的选项&#xff0c;从Default改为Disabled即可成功解决 亲测这个方法不行 解决方案 点击右上角的3个…

力扣每日一题 一个小组的最大实力值 线性DP

Problem: 2708. 一个小组的最大实力值 &#x1f468;‍&#x1f3eb; 灵神题解 class Solution {public long maxStrength(int[] nums) {// 初始化mn和mx为第一个元素的值long mn nums[0];long mx nums[0];// 从第二个元素开始遍历数组for (int i 1; i < nums.length; i…

vue项目生成插件的LICENSE文件

一、安装license-webpack-plugin npm install --save-dev license-webpack-plugin 二、添加webpack配置 const {LicenseWebpackPlugin} require(license-webpack-plugin)module.exports {configureWebpack: {plugins: [new LicenseWebpackPlugin()]} }三、执行npm run buil…

jpg图片怎么转换成png?值得推荐给大家的几种转换方法

jpg图片怎么转换成png&#xff1f;将jpg图像转换为png格式可以显著提升图像的质量和清晰度&#xff0c;并满足一些特殊需求&#xff0c;例如透明背景。png格式采用无损压缩&#xff0c;这意味着图像在转换过程中不会丢失任何细节或质量&#xff0c;相比于jpg的有损压缩&#xf…

day06 1.继承和多态

#include "work.h"Stack:: Stack():size(10) {data new char[size];top -1;cout <<"无参构造"<<endl; }Stack:: Stack(const char* s) {size strlen(s);data new char[size];strcpy(data,s);top size-1;cout <<"有参构造"…

Android之Handler的post方法和sendMessage的区别

目录 post 方法方法特点 sendMessage 方法方法特点 使用场景区别总结 Handler 类在 Android 中用于在不同线程之间传递消息和执行代码。它提供了两种主要的方式来执行任务&#xff1a;通过 post 方法和通过 sendMessage 方法。这两种方法有不同的使用场景和特点。 post 方法 方…

【系统架构设计】嵌入式系统设计(2)

【系统架构设计】嵌入式系统设计&#xff08;2&#xff09; 嵌入式网络系统嵌入式 Internet 的接入方式嵌入式 TCP/IP 协议栈 嵌入式数据库管理系统数据的一致性高效的事务处理数据的安全性 实时系统与嵌入式操作系统对实时系统划分根据实时性的强弱根据对错失时限的容忍程度或…

Android Google Maps

Android 谷歌地图 前言正文一、设置Google Cloud 项目二、项目配置① 设置SDK② 配置API密钥③ 配置AndroidManifest.xml 三、添加地图四、定位当前① 请求定位权限② 我的位置控件③ 获取当前位置 五、配置地图① xml配置地图② 代码配置地图③ 地图点击事件④ 管理Marker 六、…

达梦SQL 优化简介

目录 一、定位慢 SQL &#xff08;一&#xff09;开启跟踪日志记录 1.跟踪日志记录配置 &#xff08;二&#xff09;通过系统视图查看 1.SQL 记录配置 2.查询方式 二、SQL分析方法 &#xff08;一&#xff09;执行计划 1.概述 2.查看执行计划 &#xff08;二&#x…

使用LLaMA-Factory快速训练自己的专用大模型

本文聊聊 LLama-Factory&#xff0c;它是一个开源框架&#xff0c;这里头可以找到一系列预制的组件和模板&#xff0c;让你不用从零开始&#xff0c;就能训练出自己的语言模型&#xff08;微调&#xff09;。不管是聊天机器人&#xff0c;还是文章生成器&#xff0c;甚至是问答…

物联网能源管理平台

能源管理平台可以对接各种物联网设备&#xff0c;感兴趣的朋友可根据下方方式联系获取演示账号 1.介绍 2.智慧照明接入 设备远程控制&#xff0c;灯光亮度感应等模式设置&#xff0c;用电耗能分析 3.温控器接入 温控器&#xff08;空调面板&#xff09;接入&#xff0c;实现…

GB/T28181规范和JT1078交通部标差异

技术背景 好多开发者区分不太清楚GB/T28181和JT1078规范&#xff0c;实际上&#xff0c;二者在规范定义、技术特点、过检认证以及应用场景等方面均存在显著差异。两者各有其适用领域和优势&#xff0c;但在某些特定场景下也可能需要相互协作以实现更全面的监控和管理。 规范定…

C#复习之封装_静态成员

知识点一 静态成员的基本概念 知识点二 早已出现的静态成员 知识点四 静态成员的使用 知识点五 为什么可以直接点出来 //记住&#xff01; //程序中是不能无中生有的 //我们要使用的对象&#xff0c;变量&#xff0c;函数都是要在内存中分配内存空间的 //之所以要实例化对象…

TWS蓝牙耳机发展历史以及涉及的相关技术知识

TWS(True Wireless Stereo)蓝牙耳机是近年来迅速发展的消费电子产品之一。它们的诞生和发展经历了多个阶段,并涉及到多项关键技术。以下是 TWS 蓝牙耳机的发展历史及相关技术知识的详细解析。 1. TWS 蓝牙耳机的发展历史 1.1 初期阶段(2014年之前) 在 TWS 蓝牙耳机出现之…

老师怎样用微信发布月考成绩?

每当月考结束&#xff0c;老师们就开始了一项繁琐的任务——将成绩单一一私信给每位学生的家长。这不仅耗时耗力&#xff0c;还容易出错。家长们焦急等待&#xff0c;老师们则在电脑前忙碌着&#xff0c;一张张成绩单&#xff0c;一条条信息&#xff0c;重复的工作让人疲惫不堪…

Unity(2022.3.41LTS) - UI详细介绍-Scrollbar(滚动条)

目录 零.简介 一、基本功能与用途 二、组件介绍 三、使用方法 四、优化和注意事项 五.和滑动条的区别 零.简介 在 Unity 中&#xff0c;滚动条&#xff08;Scrollbar&#xff09;是一种用于实现滚动功能的 UI 组件。 一、基本功能与用途 滚动内容&#xff1a;主要用于…

【重学 MySQL】五、MySQL 的卸载

【重学 MySQL】五、MySQL 的卸载 停止MySQL服务卸载MySQL程序删除残余文件清理注册表删除环境变量配置重启电脑 MySQL的卸载过程需要仔细操作&#xff0c;以确保彻底卸载并清理所有相关文件和配置。 停止MySQL服务 打开任务管理器&#xff1a;右键点击任务栏空白处&#xff0…

模组级存储与嵌入式存储的千差万别

雷龙在多年销售CS创世 SD NAND&#xff08;也称&#xff1a;迷你型eMMC&#xff0c;小尺寸/小容量eMMC&#xff0c;贴片式T卡&#xff0c;贴片式TF卡&#xff0c;贴片式SD卡等&#xff09;的过程中&#xff0c;经常有被用户问到&#xff1a;SD NAND 与 TF卡 到底有哪些区别呢&a…