Linux——生产者消费者模型

Linux——生产者消费者模型



一、生产者消费者模型引入

在这里插入图片描述
生产者消费者模型就是好比是一个超市,超市作为货物存放的载体,而生产者将商品上架,消费者则按需购买商品
但这个结构能运行起来需要一定的规则,作为一个超市,肯定不止一个消费者和一个生产者,同一个商品有可能是不同的生产厂商,不同的消费者可能想买相同的商品,超市内同一个位置只能放一件商品,一件商品只能被一个消费者购买,还有消费的前提是货架上有生产者放的货品等等规则,要满足上述条件,需要搞清楚生产者与消费者之间的关系
生产者与消费者之间的三种关系

生产者之间是什么关系? 竞争—互斥
消费者和消费者之间? 竞争—互斥
生产和消费之间? 互斥&&同步

总结

321原则:

3:3种关系
2:2种角色,生产者(1 or n),消费者(1 or n) ——线程或者进程
1:一个交易场所,内存空间

如何理解CP问题:

生产者消费者模型将生产与消费的过程进行了解耦,也就是可以边生产边消费,而不是先生产后消费,内存空间作为一个载体相当于一个仓库,暂存着数据,支持忙闲不均,可以提高数据处理的效率,因为真正在实际中耗时间的并不是拿放数据,而是生产和处理数据的过程,拿或取数据只是一瞬间的事情,数据被拿走了就不属于临界区的共享资源了,是线程自己的事情了,多线程拿走数据就能同时进行不同数据的处理,提高并发,从而提高了效率

二、基于BlockingQueue的生产者消费者模型

2.1 条件变量

当一个线程互斥地访问某个变量时,它可能发现在其它线程改变状态之前,它什么也做不了
例如一个线程访问队列时,发现队列为空,它只能等待,只到其它线程将一个节点添加到队列中,这种情
况就需要用到条件变量

理解条件变量:

假设线程A是苹果生产商,线程B与C恰好都需要买苹果,但此时恰好商店里的苹果断货了,需要等待线程A来补货,由于苹果是公共资源,我们知道多线程访问公共资源需要保持互斥,所以由于线程B先来到商店,所以B先申请了锁,但B发现苹果没货,又只能又释放了锁,可是B并不知道什么时候苹果有货,只能一直申请锁,访问资源发现没货,然后释放锁,如此反复进行,导致线程C并没有机会访问共享资源,C就产生了饥饿问题,因为B一直在毫无意义的申请共享资源,这就导致了资源的浪费

条件变量就像一个超市里的铃铛一样,当线程B与C在访问资源发现没货的时候就不再一直申请锁和释放锁了,而是在铃铛下排队,当有货的时候铃铛就会响,也就是唤醒B和C线程,进行消费

条件变量的作用:

  1. 不做无效的锁申请
  2. 执行具有顺序

条件变量的本质:
在这里插入图片描述
条件变量的接口:

初始化:
int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr_t *restrict attr);
参数:
cond:要初始化的条件变量
attr:NULL

销毁:
int pthread_cond_destroy(pthread_cond_t *cond)

等待条件满足:
int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex);
参数:
cond:要在这个条件变量上等待
mutex:互斥量,也就是锁,这里需要传锁是因为在等待之前申请了锁,等待的时候需要释放锁,唤醒后需要重新申请锁

唤醒等待:
int pthread_cond_broadcast(pthread_cond_t *cond);——将等待的线程全部唤醒,即广播
int pthread_cond_signal(pthread_cond_t *cond);——唤醒一个线程

2.2 实现原理

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

上文中我们介绍了生产者消费者模型,简单来说,我们需要满足321原则

321原则:

3:3种关系
2:2种角色,生产者(1 or n),消费者(1 or n) ——线程或者进程
1:一个交易场所,内存空间

  1. 队列我们可以使用STL中自带的queue,值得注意的是,这个队列是生产者和消费者共有的,所以生产者和消费者在维护这个队列的时候,需要保持互斥性
  2. 当队列为空的时候无法消费,当队列为满的时候无法生产,这是拥塞的规则,我们需要用条件变量来约束
  3. 当生产者Push的时候可以唤醒消费者消费,当消费者Pop的时候可以唤醒生产者生产

2.3 实现代码

BlockQueue.hpp

#pragma once
#include <queue>
#include "LockGuard.hpp"


#define MaxSize 5

template <class T>
class BlockQueue
{
   
public:
    BlockQueue(int sz = MaxSize)
        : _capacity(sz)
    {
   
        pthread_mutex_init(&_mutex, nullptr);

        pthread_cond_init(&_pcond, nullptr);
        pthread_cond_init(&_ccond, nullptr);
    }

    void Push(const T &data)
    {
   
        //pthread_mutex_lock(&_mutex);
        LockGuard lock(&_mutex);

        while (IsFull())
        {
   
            pthread_cond_wait(&_pcond, &_mutex);
        }

        _q.push(data);

        pthread_cond_signal(&_ccond);

        //pthread_mutex_unlock(&_mutex);
    }

    void Pop(T &data)
    {
   
        //pthread_mutex_lock(&_mutex);
        LockGuard lock(&_mutex);
        
        while (IsEmpty())
        {
   
            pthread_cond_wait(&_ccond, &_mutex);
        }

        data = _q.front();
        _q.pop();

        pthread_cond_signal(&_pcond);

        //pthread_mutex_unlock(&_mutex);
    }

    bool IsEmpty()
    {
   
        if (_q.size() == 0)
        {
   
            return true;
        }
        else
        {
   
            return false;
        }
    }

  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值