muduo源碼分析:無界隊列和有界隊列(消費者-生產者)

muduo庫的隊列很簡單:

無界隊列

put --生產

take--消費

size--返回現在的產品數

產品存放在deque裏面

無界隊列可以一直生產put,所以不用等待隊列中有位置才生產,每生產一個產品就notify,只要通知一個消費者,不需要通知所有人

只有消費take時要判斷有無產品,所以只需要一個mutex鎖,一個條件變量notEmpty,消費者用來等待notEmpty有產品可以消費

#ifndef MUDUO_BASE_BLOCKINGQUEUE_H
#define MUDUO_BASE_BLOCKINGQUEUE_H

#include <muduo/base/Condition.h>
#include <muduo/base/Mutex.h>

#include <boost/noncopyable.hpp>
#include <deque>
#include <assert.h>

namespace muduo
{
template<typename T>
class BlockingQueue : boost::noncopyable
{
  public:
	explicit BlockingQueue()
		  :mutex_(),
		   notEmptyCond_(mutex_),
		   queue_()
	{}
	void put(const T& t)
	{
		{
			MutexLockGuard lock(mutex_);
			queue_.push_back(t);
		}
		notEmptyCond_.notify();//不需要notifyAll通知所有的,如果有阻塞,代表原先爲空,現在只生產了一個,也只要一個消費來就足夠了
	}
	T take()
	{
		MutexLockGuard lock(mutex_);
		while(queue_.empty())
		  notEmptyCond_.wait();
		assert(!queue_.empty());
		T front=queue_.front();
		queue_.pop_front();
		return front;
	}
	size_t size() const
	{
		MutexLockGuard lock(mutex_);
		return queue_.size();
	}

  private:
	mutable MutexLock mutex_;
	Condition notEmptyCond_;
	std::deque<T> queue_;
};

}

#endif

有界隊列

有界隊列用boost::circular_buffer<T>  queue_實現
因爲有界,所以生產的時候要等待有位置才能生產--用條件變量notFull
消費時用notEmpty條件變量等待有產品可以消費。
生產者和消費者處理的資源都是同一個queue_,所以是兩個條件變量(一個等待非空,一個等待非滿),但是隻要一把mutex_鎖,兩個條件變量共用一把鎖
// Use of this source code is governed by a BSD-style license
// that can be found in the License file.
//
// Author: Shuo Chen (chenshuo at chenshuo dot com)

#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 <boost/noncopyable.hpp>
#include <assert.h>

namespace muduo
{

template<typename T>
class BoundedBlockingQueue : boost::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(); // TODO: move outside of lock
  }

  T take()
  {
    MutexLockGuard lock(mutex_);
    while (queue_.empty())
    {
      notEmpty_.wait();
    }
    assert(!queue_.empty());
    T front(queue_.front());
    queue_.pop_front();
    notFull_.notify(); // TODO: move outside of lock
    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_;
  Condition                  notFull_;
  boost::circular_buffer<T>  queue_;
};

}

#endif  // MUDUO_BASE_BOUNDEDBLOCKINGQUEUE_H

如果不用boost的circular_buffer。可以用數組實現,putIndex=0 將要生產的位置;takeInex=0 將要讀的位置,isFull=false 產品是否滿了 , isEmpty=ture 產品空了
put( ){
     lock( );
     produce;//putIndex=0,剛開始直接生產沒問題,但是把wait寫後面在(2)的位置就錯了---------------(1)
     notify(notEmpty);
     putIIndex++;
     putIndex%=maxSize;
     isEmpty=fase;
     if(putIndex==takeIndex) //沒有位置可以生產了
     {
            isFull=true;
            wait(notFull); //不可以,錯了---------------------------------------(2)
      } 
}
錯誤1:put操作,把產品都已經生產了加進去了,但是卻還阻塞了,不返回。不符合常規邏輯
錯誤2:第一次 putIndex=0,剛開始直接生產沒問題(加了鎖,多個線程也沒問題,是串行的)。但是後面有問題:前一個線程剛把隊列填滿了,自己阻塞了,但其他線程卻沒有判斷就直接生產了,而事實上隊列已經滿了。所以應該要下面這樣:
put( ){
     lock( );  
      while( isFull ) // 沒有位置可以生產了
      {
          wait(notFull); 
      } 
     produce;
    isEmpty=fase;
     putIIndex++;
     putIndex%=maxSize;
     if(putIndex==takeIndex) //滿了
          isFull=true;
     notify(notEmpty);
}

take()
{
   lock( );
   while(isEmpty)
         wait(notEmpty);//不能直接消費
   cosumer;
   isFull=false;
   takeIndex++;
   takeIndex%=maxSize;   
   if(putIndex==takeIndex) //沒有產品消費了,空了
            isEmpty=true;
   notify(notFull);
}

參考:c++教程網

           muduo網絡庫

           linux多線程服務器端編程》.陳碩


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章