【lesson56】生产者消费者模型

学习生产者消费者模型过程中要回答的两个问题

1.条件变量是在条件满足的时候,会唤醒指定的线程---->我们怎么知道条件是否满足呢?
2.mutex的意义

生产者消费者模型的概念

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

生产者消费者模型:
在这里插入图片描述
生产者消费者模型优点
解耦
支持并发
支持忙闲不均

上面的优点全都是因为有一个仓库的存在。
生产者消费者模型类比:
在这里插入图片描述
消费者去超市买商品。
厂家往超市放商品
而这就是生产者消费者模型它的核心意义是提高了效率,并且通过超市让消费者和厂家进行了解耦合的关系,不用让消费者和厂家进行沟通。

超市本质就是一个商品缓冲区。

生产者消费者模型有:
2种角色:生产者和消费者
1种交易场所:超市
3中关系:
生产者和生产者-----竞争且互斥
消费者和消费者----竞争且互斥
生产者和消费者----互斥且同步

消费者和生产者由线程承担----给线程进行角色化
超市:某种数据结构—表示缓冲区
商品:数据

生产者生产的数据是从哪里来的?
消费者如何使用发送过来的数据?
而在现实生活中生产数据和发送数据的过程都需要花时间。

1.条件变量是在条件满足的时候,会唤醒指定的线程---->我们怎么知道条件是否满足呢?消费者和生产者各自知道。
超市没有商品了,消费者知道。(因为消费者去超市买不到商品)
超市商品满了,生产者知道(因为生产者刚想超市放了一堆商品)
2.mutex的意义
保护临界资源

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

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

编码实现

Common.h

#pragma once
#include <iostream>
#include <pthread.h>
#include <queue>
#include <unistd.h>
#include <ctime>
#include <functional>


#define gDefaultCap  5//阻塞队列默认的数据个数
#define CTNUM 1//消费者线程个数
#define PTNUM 1//生产者线程个数

LockGuard.hpp

//自己封装的锁,定义后会自动初始化和销毁
#pragma once
#include "Common.h"

class Mutex
{
public:
    Mutex()
    {
        pthread_mutex_init(&_mtx,nullptr);
    }

    pthread_mutex_t& GetMutex()
    {
        return _mtx;
    }

    ~Mutex()
    {
        pthread_mutex_destroy(&_mtx);
    }
private:
    pthread_mutex_t _mtx;
};

//RAII风格,会自动加锁和解锁
class LockGuard
{
public:
    LockGuard(Mutex* mtx)
    :_mtx(mtx)
    {
        pthread_mutex_lock(&_mtx->GetMutex());
        //std::cout << "lock" << std::endl;
    }


    ~LockGuard()
    {
        pthread_mutex_unlock(&_mtx->GetMutex());
        //std::cout << "unlock" << std::endl;
    }
private:
    Mutex* _mtx;
};

Condtion.hpp

#include "Common.h"
//自己封装的条件变量,定义后会自动初始化和销毁
class Condtion
{
public:
    Condtion()
    {
        pthread_cond_init(&_cond,nullptr);
    }

    pthread_cond_t& GetCond()
    {
        return _cond;
    }

    ~Condtion()
    {
        pthread_cond_destroy(&_cond);
    }
private:
    pthread_cond_t _cond;
};

BlockQueue.hpp

#pragma once
#include "Common.h"
#include "LockGuard.hpp"
#include "Condtion.hpp"

template<class T>
class BlockQueue
{
private:
    bool Empty()
    {
        return bq.size() == 0;
    }
    bool Full()
    {
        return bq.size() == _capacity;
    }
public:
    BlockQueue(int capacity = gDefaultCap)
    :_capacity(capacity)
    {}

    void Push(T& in)
    {
        //RAII风格加锁,会自动解锁
        LockGuard lockgrard(&_mtx);
        //条件不满足就一直等待
        while(Full())
        {
            pthread_cond_wait(&_FullCond.GetCond(),&_mtx.GetMutex());
        }
        bq.push(in);
        //唤醒消费者线程
        pthread_cond_signal(&_EmptyCond.GetCond());
    }
    void Pop(T* out)
    {
        //RAII风格加锁,会自动解锁
        LockGuard lockgrard(&_mtx);
        //条件不满足就一直等待
        while(Empty())
        {
            pthread_cond_wait(&_EmptyCond.GetCond(),&_mtx.GetMutex());
        }
        *out = bq.front();
        bq.pop();
        //唤醒生产者线程
        pthread_cond_signal(&_FullCond.GetCond());

    }

    ~BlockQueue()
    {}
private:
    std::queue<T> bq;//阻塞队列
    int _capacity;//阻塞队列的最大容量
    Mutex _mtx;//自己封装的锁,会自动初始化和销毁
    Condtion _EmptyCond;//自己封装的条件变量,会自动初始化和销毁
    Condtion _FullCond;//自己封装的条件变量,会自动初始化和销毁
};

Task.hpp

#pragma once
#include "Common.h"

typedef std::function<int(int, int)> func_t;

class Task
{
public:
    Task()
    {}
    
    Task(int x, int y, func_t func)
        : _x(x),
          _y(y),
          _func(func)
    {}
	
	//仿函数
    int operator()()
    {
        return _func(_x, _y);
    }

public:
    int _x;
    int _y;
    func_t _func;
};

ConProd.cc

#include "Common.h"
#include "BlockQueue.hpp"
#include "Task.hpp"

int myAdd(int x,int y)
{
    return x+y;
}

void* ConsumerRoutine(void* args)
{
    BlockQueue<Task>* bq = (BlockQueue<Task>*)args;

    Task t;
    while(true)
    {
        //获取一个任务
        bq->Pop(&t);
        std::cout << "[" << pthread_self() << "] 获取一个任务:" << t._x << " + " << t._y << " = " << t() << std::endl;
        sleep(1);
    }
}
void* ProducterRoutine(void* args)
{
    BlockQueue<Task>* bq = (BlockQueue<Task>*)args;
    while(true)
    {
        //制作一个任务
        int x = rand() % 100 + 1;
        int y = rand()%30 + 1;
        
        Task t(x,y,myAdd);
        std::cout << "[" << pthread_self() << "] 制作一个任务:" << x << " + " << y << " =?" << std::endl;
        bq->Push(t);
        sleep(1);
    }
}
int main()
{
    srand((unsigned int)time(nullptr) ^ getpid() ^ 0X3333);

    //定义消费者线程和生成者线程的id数组
    pthread_t Consumer[CTNUM];
    pthread_t Producter[PTNUM];

    //创建阻塞队列
    BlockQueue<Task>* bqueue = new BlockQueue<Task>;

    //创建消费者线程
    for(int i = 0; i < CTNUM; i++)
    {
        pthread_create(Consumer + i,nullptr,ConsumerRoutine,(void*)bqueue);
    }
   
   //创建生产者线程
   for(int i = 0; i < PTNUM; i++)
    {
        pthread_create(Producter + i,nullptr,ProducterRoutine,(void*)bqueue);
    }
    
    //等待消费者线程
    for(int i = 0; i < CTNUM; i++)
    {
        pthread_join(Consumer[i],nullptr);
    }
    
    //等待生产者线程
    for(int i = 0; i < CTNUM; i++)
    {
        pthread_join(Producter[i],nullptr);
    }
    

    return 0;
}
  • 20
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值