【Linux】生产者消费者模型

本文详细介绍了Linux环境下的生产者消费者模型,包括其基本概念、特点和优点。通过阻塞队列 BlockingQueue 实现了生产者消费者模型,并给出了C++使用queue模拟阻塞队列的代码示例,展示了单生产者单消费者以及多生产者多消费者的场景,强调了解耦、并发支持和忙闲不均的处理能力。
摘要由CSDN通过智能技术生成

Linux生产者与消费者模型

什么是生产者与消费者模型

生产者消费者模型就是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力。这个阻塞队列就是用来给生产者和消费者解耦的

生产者与消费者其实在我们生活中很常见,比如工厂生产商品,我们购买商品,那么这里工厂就是生产者,我们就是消费者。还有一个仓库,当生产者生产出货物,将货物放入仓库,我们消费时货物从仓库发出。这三者共同组成生产者和消费者模型。

请添加图片描述

生产者消费者模型特点

  • 三种关系生产者和生产者之间的互斥关系消费者和消费者之间的互斥关系生产者和消费者之间同步关系
  • 两个角色:生产者和消费者(通常是两个线程)
  • 一个交易场所:指这个模型里的仓库,也就是内存里的一块缓冲区

生产者与消费者模型的优点

  • 解耦

有了缓冲区之后,生产者和消费者之间不会直接通信,依赖关系将大大减少,如果其中一方出现问题,不会对另一方产生很大影响

  • 支持并发

如果没有这个“仓库”,那么生产者和消费者之间是串行执行的,一方生产一个,一方消费一个,如果一方出现问题或者阻塞,那么整个程序都会阻塞,效率就会降低,而有了这个缓冲区,生产者只管生产,消费者只需从仓库内消费,两个线程同时操作,互不影响,效率提高

  • 支持忙闲不均

如果生产者生产的速度不均衡,导致时快时慢,让消费者来不及消费,那么缓冲区的作用就体现出来了,让消费者来不及处理的数据先放在缓冲区中,等消费者有时间再去处理。减少忙的时间太忙,闲的时间太闲的现象。

基于阻塞队列(BlockingQueue)的生产者消费者模型

阻塞队列(BlockingQueue)

在多线程编程中阻塞队列(Blocking Queue)是一种常用于实现生产者和消费者模型的数据结构。其与普通的队列区别在于,当队列为空时,从队列获取元素的操作将会被阻塞,直到队列中被放入了元素;当队列满时,往队列里存放元素的操作也会被阻塞,直到有元素被从队列中取出(以上的操作都是基于不同的线程来说的,线程在对阻塞队列进程操作时会被阻塞)

请添加图片描述

这个阻塞队列就是模型里"仓库"的具体实现,这个"仓库"也可以是别的数据结构

用 C++ queue 模拟阻塞队列的生产消费模型

单生产者单消费者

代码实现

  • makeflie
main:main.cpp
        g++ -o $@ $^ -std=c++11 -lpthread

.PHONY:clean
clean:
        rm -f main
  • BlockQueue.hpp
#ifndef __QUEUE_BLOCK_H__
#define __QUEUE_BLOCK_H__

#include<iostream>
#include<pthread.h>
#include<unistd.h>
#include<queue>

template<class T>
class BlockQueue {
public:
    BlockQueue(size_t capacity) :_capacity(capacity) {
        pthread_mutex_init(&lock, nullptr);
        pthread_cond_init(&p_cond, nullptr);
        pthread_cond_init(&c_cond, nullptr);
    }
public:
    void WakeUpConsumer() {
        pthread_cond_signal(&c_cond);
        std::cout << "wake up Consumer" << std::endl;
    }
    void WakeUpProductor() {
        pthread_cond_signal(&p_cond);
        std::cout << "wake up productor" << std::endl;
    }
    void ProductorWait() {
        pthread_cond_wait(&p_cond, &lock);
        std::cout << "productor wait" << std::endl;
    }
    void ConsumerWait() {
        pthread_cond_wait(&c_cond, &lock);
        std::cout << "consumer wait" << std::endl;
    }
    void LockQueue() {
        pthread_mutex_lock(&lock);
    }
    void UnLockQueue() {
        pthread_mutex_unlock(&lock);
    }
    void put(const T& t) {
        LockQueue();
        while (IsFull()) {
            WakeUpConsumer();
            ProductorWait();
        }
        _q.push(t);
        UnLockQueue();
    }

    void take(T& t) {
        LockQueue();
        while (IsEmpty()) {
            WakeUpProductor();
            ConsumerWait();
        }
        t = _q.front();
        _q.pop();
        UnLockQueue();
    }


    bool IsFull() {
        return _q.size() >= _capacity;
    }
    bool IsEmpty() {
        return _q.empty();
    }


    ~BlockQueue() {
        pthread_mutex_destroy(&lock);
        pthread_cond_destroy(&c_cond);
        pthread_cond_destroy(&p_cond);
    }
private:
    std::queue<T> _q;
    size_t _capacity;
    pthread_mutex_t lock;
    pthread_cond_t p_cond;
    pthread_cond_t c_cond;
};
#endif
  • main.cpp
#include"BlockQueue.hpp"
using namespace std;
void* Productor_run(void* arg){
    sleep(1);
    BlockQueue<int>* pbq=(BlockQueue<int>*)arg;
    int t=0;
    while(true){
        t++;
        pbq->put(t);
        sleep(1);
        cout<<"Productor put data:"<<t<<endl;
    }
}
void* Consumer_run(void* arg){
    BlockQueue<int>* pbq=(BlockQueue<int>*)arg;
    while(true){
        int t=0;
        pbq->take(t);
        sleep(1);
        cout<<"Consumer take data:"<<t<<endl;
    }
}
int main(){
  BlockQueue<int> bq(5);
  pthread_t p,c;
  pthread_create(&p,nullptr,Productor_run,(void*)&bq);
  pthread_create(&c,nullptr,Consumer_run,(void*)&bq);

  pthread_join(p,nullptr);
  pthread_join(c,nullptr);

  return 0;
}

请添加图片描述

多消费者,多生产者,生产和消费任务 Task

前面是一个生产者一个消费者,两者之间是同步关系,那么增加了消费者和生产者,就要维护生产者和消费者之间的互斥关系

  • BlockQueue.hpp
#ifndef __QUEUE_BLOCK_H__
#define __QUEUE_BLOCK_H__
#include<iostream>
#include<pthread.h>
#include<unistd.h>
#include<queue>
//任务封装
class Task {
public:
    int _x;
    int _y;
public:
    Task() {}
    Task(int x, int y) :_x(x), _y(y)
    {}

    int run() {
        return _x + _y;
    }
};


template<class T>
class BlockQueue {
private:
    std::queue<T> _q;
    size_t _capacity;
    pthread_mutex_t lock;
    pthread_cond_t p_cond;
    pthread_cond_t c_cond;
public:
    BlockQueue(size_t capacity) :_capacity(capacity) {
        pthread_mutex_init(&lock, nullptr);
        pthread_cond_init(&p_cond, nullptr);
        pthread_cond_init(&c_cond, nullptr);
    }
private:
    void WakeUpConsumer() {
        pthread_cond_signal(&c_cond);
        std::cout << "wake up Consumer" << std::endl;
    }
    void WakeUpProductor() {
        pthread_cond_signal(&p_cond);
        std::cout << "wake up productor" << std::endl;
    }
    void ProductorWait() {
        pthread_cond_wait(&p_cond, &lock);
        std::cout << "productor wait" << std::endl;
    }
    void ConsumerWait() {
        pthread_cond_wait(&c_cond, &lock);
        std::cout << "consumer wait" << std::endl;
    }
    void LockQueue() {
        pthread_mutex_lock(&lock);
    }
    void UnLockQueue() {
        pthread_mutex_unlock(&lock);
    }
    bool IsFull() {
        return _q.size() >= _capacity;
    }
    bool IsEmpty() {
        return _q.empty();
    }
public:
    void Put(const T& t) {
        //维护生产者和消费者间的互斥关系
        LockQueue();
        while (IsFull()) {
            WakeUpConsumer();
            ProductorWait();
        }
        _q.push(t);
        UnLockQueue();
    }

    void Take(T& t) {
        //维护生产者和消费者间的互斥关系
        LockQueue();
        while (IsEmpty()) {
            WakeUpProductor();
            ConsumerWait();
        }
        t = _q.front();
        _q.pop();
        UnLockQueue();
    }
    ~BlockQueue() {
        pthread_mutex_destroy(&lock);
        pthread_cond_destroy(&c_cond);
        pthread_cond_destroy(&p_cond);
    }
};
#endif
  • main.cpp
#include"BlockQueue.hpp"
using namespace std;
//两个角色用两把锁
pthread_mutex_t p_lock;
pthread_mutex_t c_lock;

void* Productor_run(void* arg){
    sleep(1);
    BlockQueue<Task>* pbq=(BlockQueue<Task>*)arg;

    while(true){
        //维护多个生产者之间的互斥关系
        pthread_mutex_lock(&p_lock);
        int x = rand()%10+1;
        int y = rand()%100+1;
        Task t(x,y);
        pbq->Put(t);
        cout<<"Producter id:"<<pthread_self()<<"  "<<"Producter Task is "<<x<<"+"<<y<<"=?"<<endl;
        pthread_mutex_unlock(&p_lock);
        sleep(1);
    }
}
void* Consumer_run(void* arg){
    BlockQueue<Task>* pbq=(BlockQueue<Task>*)arg;
    while(true){
        //维护多个消费者之间的互斥关系
        pthread_mutex_lock(&c_lock);
        Task t;
        pbq->Take(t);
        cout<<"Consumer id:"<<pthread_self()<<" "<<"Consumer Task is:"<<t._x<<"+"<<t._y<<"="<<t.run()<<endl;
        pthread_mutex_unlock(&c_lock);
        sleep(1);
    }
}

int main(){
  BlockQueue<Task> bq(5);
  pthread_t p1,p2,p3,c1,c2,c3;
  pthread_create(&p1,nullptr,Productor_run,(void*)&bq);
  pthread_create(&p2,nullptr,Productor_run,(void*)&bq);
  pthread_create(&p3,nullptr,Productor_run,(void*)&bq);
  pthread_create(&c1,nullptr,Consumer_run,(void*)&bq);
  pthread_create(&c2,nullptr,Consumer_run,(void*)&bq);
  pthread_create(&c3,nullptr,Consumer_run,(void*)&bq);

  pthread_join(p1,nullptr);
  pthread_join(p2,nullptr);
  pthread_join(p3,nullptr);
  pthread_join(c1,nullptr);
  pthread_join(c2,nullptr);
  pthread_join(c3,nullptr);
  
  pthread_mutex_destroy(&c_lock);
  pthread_mutex_destroy(&p_lock);
  
  return 0;
}

请添加图片描述

也就是说,这个模型下,要让生产者和消费者处理任务时,只需提供对应的Task类,类中实现数据的处理方法即可

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xiaomage1213888

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值