这两个类其实就是多生产者-多消费者模型(使用条件变量实现)。
BlockingQueue类(无界缓冲区)
基于std::deque实现。
BlockingQueue.h
很简单
因为无界,所以一个条件变量就够了。
#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:
BlockingQueue()
: mutex_(),
notEmpty_(mutex_),
queue_()
{
}
void put(const T& x)//produce
{
MutexLockGuard lock(mutex_);
queue_.push_back(x);
notEmpty_.notify(); // TODO: move outside of lock
}
T take()//consume
{
MutexLockGuard lock(mutex_);
// always use a while-loop, due to spurious wakeup
while (queue_.empty())//while是为了处理多个消费者的情况(很好想,比如一个消费者的pthread_cond_wait中先抢到了锁,并且消费完后又重新释放锁,那么此时另一个消费者的pthread_cond_wait中虽然拿到了锁,但是可能此时产品却没有了)
{
notEmpty_.wait();
}
//既要拿到锁,同时又要保证队列非空,都满足后才能执行接下来的代码(即开始消费)
assert(!queue_.empty());
T front(queue_.front());
queue_.pop_front();
return front;
}
size_t size() const
{
//常成员函数要修改mutex_,所以mutex_必须是mutable
MutexLockGuard lock(mutex_);
return queue_.size();
}
private:
mutable MutexLock mutex_;
Condition notEmpty_;
std::deque<T> queue_;
};
}
#endif // MUDUO_BASE_BLOCKINGQUEUE_H
BoundedBlockingQueue类(有界缓冲区)
基于boost::circular_buffer实现。
BoundedBlockingQueue.h
也很简单
因为有界,所以要两个条件变量来控制。
#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)//produce
{
MutexLockGuard lock(mutex_);
while (queue_.full())
{
notFull_.wait();
}
assert(!queue_.full());
queue_.push_back(x);
notEmpty_.notify(); // TODO: move outside of lock
}
T take()//consume
{
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
测试
例:主线程生产,5个子线程消费。
BoundedBlockingQueue_test.cc
#include <muduo/base/BoundedBlockingQueue.h>
#include <muduo/base/CountDownLatch.h>
#include <muduo/base/Thread.h>
#include <boost/bind.hpp>
#include <boost/ptr_container/ptr_vector.hpp>
#include <string>
#include <stdio.h>
class Test
{
public:
Test(int numThreads)
: queue_(20),
latch_(numThreads),
threads_(numThreads)
{
for (int i = 0; i < numThreads; ++i)
{
char name[32];
snprintf(name, sizeof name, "work thread %d", i);
threads_.push_back(new muduo::Thread(
boost::bind(&Test::threadFunc, this), muduo::string(name)));
}
for_each(threads_.begin(), threads_.end(), boost::bind(&muduo::Thread::start, _1));
}
void run(int times)//主线程要完成的任务
{
printf("waiting for count down latch\n");
latch_.wait();//门闩对象阻塞主线程
printf("all threads started\n");
for (int i = 0; i < times; ++i)
{
char buf[32];
snprintf(buf, sizeof buf, "hello %d", i);
queue_.put(buf);//produce
printf("tid=%d, put data = %s, size = %zd\n", muduo::CurrentThread::tid(), buf, queue_.size());
}
}
void joinAll()
{
for (size_t i = 0; i < threads_.size(); ++i)
{
queue_.put("stop");
}
for_each(threads_.begin(), threads_.end(), boost::bind(&muduo::Thread::join, _1));
}
private:
void threadFunc()//子线程中需要完成的任务
{
printf("tid=%d, %s started\n",
muduo::CurrentThread::tid(),
muduo::CurrentThread::name());
latch_.countDown();
bool running = true;
while (running)
{
std::string d(queue_.take());//consume
printf("tid=%d, get data = %s, size = %zd\n", muduo::CurrentThread::tid(), d.c_str(), queue_.size());
running = (d != "stop");
}
printf("tid=%d, %s stopped\n",
muduo::CurrentThread::tid(),
muduo::CurrentThread::name());
}
muduo::BoundedBlockingQueue<std::string> queue_;//假设产品类型是string
muduo::CountDownLatch latch_;
boost::ptr_vector<muduo::Thread> threads_;
};
int main()
{
printf("pid=%d, tid=%d\n", ::getpid(), muduo::CurrentThread::tid());
Test t(5);
t.run(100);
t.joinAll();
printf("number of created threads %d\n", muduo::Thread::numCreated());
}
可以看到,有界缓冲区的size并没有超出我们预先设定的值20。