【muduo】base库之 BlockingQueue和BounderBlockingQueue

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

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

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

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

【muduo】base库之 BlockingQueue和BounderBlockingQueue_第1张图片

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

【muduo】base库之 BlockingQueue和BounderBlockingQueue_第2张图片

二、BlockingQueue

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

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

【muduo】base库之 BlockingQueue和BounderBlockingQueue_第3张图片

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

【muduo】base库之 BlockingQueue和BounderBlockingQueue_第4张图片

 

源代码:

#ifndef MUDUO_BASE_BLOCKINGQUEUE_H
#define MUDUO_BASE_BLOCKINGQUEUE_H

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

#include 
#include 

//无界队列的实现

namespace muduo
{

template
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     queue_ GUARDED_BY(mutex_);	   //仓库;   使用了deque双端队列
};

}  // namespace muduo

#endif  // MUDUO_BASE_BLOCKINGQUEUE_H

 

 

三、BounderBlockingQueue

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

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

【muduo】base库之 BlockingQueue和BounderBlockingQueue_第5张图片

【muduo】base库之 BlockingQueue和BounderBlockingQueue_第6张图片

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

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

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

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

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

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

在这里插入图片描述

 

源代码:

#ifndef MUDUO_BASE_BOUNDEDBLOCKINGQUEUE_H
#define MUDUO_BASE_BOUNDEDBLOCKINGQUEUE_H

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

#include 
#include 

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

namespace muduo
{

template
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  queue_ GUARDED_BY(mutex_);		 //使用了boost库的环形缓冲区
};

}  // namespace muduo

#endif  // MUDUO_BASE_BOUNDEDBLOCKINGQUEUE_H

 

你可能感兴趣的:(muduo源码剖析)