【muduo】base库之 BlockingQueue和BounderBlockingQueue

一、生产者---消费者模型

无界缓冲区与有界缓冲区的封装,本质就是生产者---消费者模型。生产者消费者模型一般有两种实现方式,可以利用信号量也可以利用条件变量实现,muduo库采用条件变量实现。

        有界缓冲区是指生产者在向仓库添加数据时要先判断仓库是否已满,如果已满则通知消费者来取走数据;消费者在消费时,先判断仓库是否已空,如果是则通知生产者生产数据。

       在无界缓冲中,生产者不用关心仓库是否已满,只需添加数据;消费者在判断仓库已空时要等待生产者的信号,这时只需用一个信号量。

在这里插入图片描述

      多线程环境中,通过队列可以很容易实现数据共享,比如经典的“生产者”和“消费者”模型中,通过队列可以很便利地实现两者之间的数据共享。假设我们有若干生产者线程,另外又有若干个消费者线程。如果生产者线程需要把准备好的数据共享给消费者线程,利用队列的方式来传递数据,就可以很方便地解决他们之间的数据共享问题。但如果生产者和消费者在某个时间段内,万一发生数据处理速度不匹配的情况呢?理想情况下,如果生产者产出数据的速度大于消费者消费的速度,并且当生产出来的数据累积到一定程度的时候,那么生产者必须暂停等待一下(阻塞生产者线程),以便等待消费者线程把累积的数据处理完毕,反之亦然。

在这里插入图片描述

二、BlockingQueue

      BlockingQueue很好的解决了多线程中,如何高效安全“传输”数据的问题。通过这些高效并且线程安全的队列类,为我们快速搭建高质量的多线程程序带来极大的便利。

BlockingQueue它是线程安全的,我们在外部调用它时无需加锁。它的类图如下:

该类是没有边界的,即在这个队列中,如果为空,是不能进行取走的操作,但是可以一直的往队列中进行添加

在这里插入图片描述

 

源代码:

#ifndef MUDUO_BASE_BLOCKINGQUEUE_H
#define MUDUO_BASE_BLOCKINGQUEUE_H

#include "muduo/base/Condition.h"
#include "muduo/base/Mutex.h"

#include <deque>
#include <assert.h>

//无界队列的实现

namespace muduo
{

template<typename T>
class BlockingQueue : noncopyable
{
 public:
  BlockingQueue()
    : mutex_(),				//先初始化互斥量
      notEmpty_(mutex_),	//再用互斥量初始化信号
      queue_()
  {
  }

  //生产产品(往阻塞队列(缓冲区)放任务)
  void put(const T& x)
  {
    MutexLockGuard lock(mutex_); //先加上锁对队列进行保护,构造函数中调用lock,析构函数会自动调用unlock
    queue_.push_back(x);		 //产品放进队列
    notEmpty_.notify(); 		 //每添加一个元素,就通知所有的线程(当前缓冲区不为空,可以来取任务了);
								 //队列不为空,通知消费者可以进行消费;通知等待的线程,实现线程同步
    // http://www.domaigne.com/blog/computing/condvars-signal-with-mutex-locked-or-not/
  }
  
  
  void put(T&& x)	//右值
  {
    MutexLockGuard lock(mutex_);
    queue_.push_back(std::move(x));
    notEmpty_.notify();
  }

  //消费产品(向缓冲区取任务)
  T take()
  {
    MutexLockGuard lock(mutex_); //加锁保护队列(线程安全)
    // always use a while-loop, due to spurious wakeup
    while (queue_.empty())		//如果队列为空(仓库已空)
    {
      notEmpty_.wait(); 		//等待生产者信号(缓冲区为空则无法取任务)
    }
    assert(!queue_.empty());	//确保队列非空
    T front(std::move(queue_.front()));	//取出队首元素
    queue_.pop_front();			//将队首元素弹出
    return front;				//返回队首元素
  }

  size_t size() const   /*可能有多个线程访问所以需要保护*/
  {
    MutexLockGuard lock(mutex_);	//加锁保护
    return queue_.size();			//返回队列大小
  }

 private:
  mutable MutexLock mutex_; 	//互斥锁(量);mutable表示可变的,修饰后可以改变const的特性
  Condition         notEmpty_ GUARDED_BY(mutex_);  //条件变量(信号量)
  std::deque<T>     queue_ GUARDED_BY(mutex_);	   //仓库;   使用了deque<T>双端队列
};

}  // namespace muduo

#endif  // MUDUO_BASE_BLOCKINGQUEUE_H

 

 

三、BounderBlockingQueue

BoundBlockingQueue有界阻塞队列,实际上就是实现了一个循环队列,功能和上面的BlockingQueue都是一样的。

muduo库实现该队列实际上是内部把boost::circular_buffer类作为底层数据结构实现的,所以说这两个阻塞队列其实没啥区别,只是底层采用不同数据结构存储数据而已。它的类图如下:

在这里插入图片描述

与BlockingQueue不同,BounderBlockingQueue是有边界的,即仓库是有限的。该类的仓库有四个状态:非空、已空;非满、已满。

当生产者生产时,会先判断仓库是否已满,如果是则等待仓库非满的信号;否则向仓库添加货物,之后通知消费者仓库非空。

当消费者取货物时会先判断仓库是否为空,如果是则等待仓库非空信号;否则取走货物,通知生产者仓库非满。

该类实现生产者消费者模型需要2个信号量,一个是非空,表示消费者可以消费了;一个是非满,表示生产者可以生产了。

当队列中没有数据的情况下,消费者端的所有线程都会被自动阻塞(挂起),直到有数据放入队列。

当队列中填满数据的情况下,生产者端的所有线程都会被自动阻塞(挂起),直到队列中有空的位置,线程被自动唤醒

在这里插入图片描述

 

源代码:

#ifndef MUDUO_BASE_BOUNDEDBLOCKINGQUEUE_H
#define MUDUO_BASE_BOUNDEDBLOCKINGQUEUE_H

#include "muduo/base/Condition.h"
#include "muduo/base/Mutex.h"

#include <boost/circular_buffer.hpp>
#include <assert.h>

//有界队列(环形队列)

namespace muduo
{

template<typename T>
class BoundedBlockingQueue : noncopyable
{
 public:
  explicit BoundedBlockingQueue(int maxSize) //参数是队列的最大容量
    : mutex_(),
      notEmpty_(mutex_),
      notFull_(mutex_),
      queue_(maxSize)
  {
  }

  void put(const T& x)
  {
    MutexLockGuard lock(mutex_);
    while (queue_.full())	//仓库已满
    {
      notFull_.wait();		//等待非满信号,即消费者消费后会通知
    }
    assert(!queue_.full());
    queue_.push_back(x);	//添加一个元素
    notEmpty_.notify();		//不为空的条件变量就通知线程,通知消费者仓库已经有货(非空)
  }

  void put(T&& x)
  {
    MutexLockGuard lock(mutex_);
    while (queue_.full())
    {
      notFull_.wait();
    }
    assert(!queue_.full());
    queue_.push_back(std::move(x));
    notEmpty_.notify();
  }

  T take()
  {
    MutexLockGuard lock(mutex_);
    while (queue_.empty())		//仓库已空
    {
      notEmpty_.wait();			//等待生产者向仓库添加货物
    }
    assert(!queue_.empty());
    T front(std::move(queue_.front()));
    queue_.pop_front();			//当删除(取走)队列中的元素的时候
    notFull_.notify();			//不为满的条件变量就通知线程,通知生产者仓库已经非满了
    return front;
  }

  bool empty() const
  {
    MutexLockGuard lock(mutex_);
    return queue_.empty();
  }

  bool full() const
  {
    MutexLockGuard lock(mutex_);
    return queue_.full();
  }

  size_t size() const
  {
    MutexLockGuard lock(mutex_);
    return queue_.size();
  }

  size_t capacity() const  	//返回容量
  {
    MutexLockGuard lock(mutex_);
    return queue_.capacity();
  }

 private:
  mutable MutexLock          mutex_;
  Condition                  notEmpty_ GUARDED_BY(mutex_);	 //非空信号量,非空可读
  Condition                  notFull_ GUARDED_BY(mutex_);	 //非满信号量,不满可写
  boost::circular_buffer<T>  queue_ GUARDED_BY(mutex_);		 //使用了boost库的环形缓冲区
};

}  // namespace muduo

#endif  // MUDUO_BASE_BOUNDEDBLOCKINGQUEUE_H

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

潇湘夜雨~

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

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

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

打赏作者

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

抵扣说明:

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

余额充值