首页 > 编程语言 >c++实现生产者&消费者的供需关系

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

时间:2024-09-03 22:25:29浏览次数:11  
标签:std 生产者 lock PID c++ _. int 供需 pool

一、生产者&消费者模式

生产者-消费者模式(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++代码可以模拟供需关系,有趣也有成就感。

标签:std,生产者,lock,PID,c++,_.,int,供需,pool
From: https://blog.csdn.net/qq_55610255/article/details/141754951

相关文章

  • Linux C++ 开发7 - GDB常用命令汇总(你想了解的都在这)
    1.运行命令2.设置断点3.查看源码4.打印表达式5.查看运行信息5.1.设置和查看运行参数的Demo6.分割窗口7.参考文档上一篇《LinuxC++开发6-GDB调试》中我们讲解了GDB的调试流程和常用的调试方法。GDB的调试指令众多,我们这里针对常用的指令做一个汇总(按功能......
  • Linux C++ 开发7 - GDB常用命令汇总(你想了解的都在这)
    1.运行命令2.设置断点3.查看源码4.打印表达式5.查看运行信息5.1.设置和查看运行参数的Demo6.分割窗口7.参考文档上一篇《LinuxC++开发6-GDB调试》中我们讲解了GDB的调试流程和常用的调试方法。GDB的调试指令众多,我们这里针对常用的指令做一个汇总(按功能分......
  • 【C++】_vector定义、_vector常用方法解析
    不管心情如何,不论今天过得怎么样,无论身在何方,请记得...微笑!......
  • C++STL
    1.1STL初识STL(StandardTemplateLibrary,标准模板库)STL从广义上分为:容器(container)算法(algorithm)迭代器(iterator)容器和算法之间通过迭代器进行无缝连接STL几乎所有代码都采用了模板类或者模板函数1.2STL六大组件STL大体分为六大组件,分别是:容器、算法、迭代器、仿函数、适配器......
  • c++
    1 C++概述1.1 C++两大编程思想1.1.1 面向对象  泛型编程1.2 C++98标准2 C++书写helloworld2.1 包含头文件  #include<iostream>标准输入输出头文件2.2 usingnamespacestd;使用标准命名空间2.3 cout<<“helloworld”<<endl; endline;2.4 面向对象三大特......
  • (D卷,100分)- 堆栈中的剩余数字(Java & JS & Python&C&C++)
    题目描述向一个空栈中依次存入正整数,假设入栈元素n(1<=n<=2^31-1)按顺序依次为nx…n4、n3、n2、n1,每当元素入栈时,如果n1=n2+…+ny(y的范围[2,x],1<=x<=1000),则n1~ny全部元素出栈,重新入栈新元素m(m=2*n1)。如:依次向栈存入6、1、2、3,当存入6、1、2时,栈底......
  • C++入门基础知识48——【关于C++函数】之Lambda 函数与表达式
    成长路上不孤单......
  • c++质因数分解
    质因数分解,最先想到了遍历1-n,找出既是质数也是因数的数。需要用到判断质数函数、while循环,复杂度三次方以上了。虽然比较慢,但是能做数学题。#include<iostream>usingnamespacestd;boolzs(intn){ for(inti=2;i<=n/2;i++){ if(n%i==0){ return......
  • C++学习笔记(大白话版)
     函数重载:名字一样,参数不一样 同一个小区,不同的家庭在小区中住不同的房子 缺省参数:写函数的时候故意不把参数写完,但是只能不写左边的,右边的必须写 如果在使用有缺省参数的函数时,给了实参值,那么就优先调用实参值 如果没有给实参,就可以用默认参数了。 函数定......
  • C++实现 || 敲桌子小游戏
    这是一个在聚会和酒桌上常玩的一个小游戏。游戏规则所有人围着桌子一个大圈,从“1”开始喊,遇到7、7的倍数或是带7的数字,就敲一下桌子(酒桌上用筷子敲下杯子),以此类推。一旦有人做错了就要接受惩罚。实现思路我们建立一个for循环,让变量在其中不断递增。在循环体内部,我们对变量进行判断,......