最近在读到陈硕的《linux多线程服务端编程》这书时,发现了两个特别好用的模板类 : BlockQueue和BlockBoundQueue,用来实现多线程中的生产者消费者问题是特别方便的。但是其源码中用到boost库,所以在这里我稍微修改下,实现如下。
这里只写出BlockBoundQueue,读者可自行写出BlockQueue
// file : blockBoundQueue.h
#ifndef YANG_BLOCKBOUNDQUEUE
#define YANG_BLOCKBOUNDQUEUE
#include
#include
#include
#include
namespace yang{
#define _FULL 4 //
template <typename _Tp>
class BlockBoundQueue
{
public:
BlockBoundQueue(size_t bound = _FULL) :bound_(bound){}
BlockBoundQueue(const BlockBoundQueue&) = delete;
BlockBoundQueue& operator=(const BlockBoundQueue&) = delete;
void push(const _Tp& value)
{
std::unique_lock<std::mutex> lck(mutex_);// 利用RAII技法,将mutex_托管给lck
while (count_ + 1 == bound_)// 用while防止虚假唤醒
{
printf("the queue is full , waiting for the consumer consuming !\n");
notFull_.wait(lck); //等待队列非满条件发生
}
count_++;
queue_.push(value);
notEmpty_.notify_one();//通知队列非空,不能用all,读者自行思考为什么
}
_Tp get()
{
std::unique_lock<std::mutex> lck(mutex_);
while (queue_.empty())
{
printf("the queue is empty , waiting for the producer producing !\n");
notEmpty_.wait(lck);//等待队列为非空
}
_Tp front(queue_.front());
queue_.pop();
count_--;
notFull_.notify_one();//通知队列为非满,请注意不能用all
return front;
}
private:
std::mutex mutex_;
std::condition_variable notEmpty_;
std::condition_variable notFull_;
std::queue<_Tp> queue_;
size_t count_{ 0 };
size_t bound_;
};
}
#endif
//在visual studio 2013上编译运行通过
分析上面代码,首先std::queue
容器并没有提供full()
这个成员函数,所以我们要自己维护一个size_t type
的变量count_
,并初始化为0 。其次,我们要禁用掉拷贝构造和赋值函数,只保留构造函数即可。最重要的两个成员函数是push()
和get()
,在这里每次进入这两个函数之后,先加锁,这里我们用到了RAII的技法来保证我们的加锁操作是异常安全的,并且通过两个条件变量notEmpty 和 notFull
的配合,实现了线程安全的出队和入队操作。
下面我们来实现下,篇幅的原因,直接上多生产者多消费者模式,因为其他的几种都是它的特列。
// file : main.cpp
#include
#include
#include
#include "BlockBoundQueue.h"
yang::BlockBoundQueue<int> blockboundqueue_;// 全局的缓冲边界队列
const int total = 16; // 商品总数
void consumer(size_t id,size_t n); // 消费者
void producer(size_t id,size_t n); // 生产者
int main()
{
std::thread consumer1(consumer,0, 5);
std::thread consumer2(consumer,1, 5);
std::thread consumer3(consumer,2, 6);
std::thread producer1(producer,0, 8);
std::thread producer2(producer,1, 8);
consumer1.join();
consumer2.join();
consumer3.join();
producer1.join();
producer2.join();
system("pause");
return 0;
}
void consumer(size_t id,size_t n)
{
for (auto i = 0; i < n; ++i)
{
std::this_thread::sleep_for(std::chrono::seconds(2));
int item = blockboundqueue_.get();
printf("the %d^th consumer thread has consumed the %d^th item\n", id,item);
}
}
void producer(size_t id,size_t n)
{
for (int i = 0; i < n; ++i)
{
blockboundqueue_.push(i);
printf("the %d^th producer thread has produced the %d^th item\n",id, i);
}
}
总结 : 利用BlockBoundQueue可以很方便的实现生产者消费者模式,并且其本身实现起来简洁优雅,我们应该学会使用。
参考资料:
Linux多线程服务端编程 陈硕著
http://www.cplusplus.com/reference/
https://baptiste-wicht.com/posts/2012/04/c11-concurrency-tutorial-advanced-locking-and-condition-variables.html
http://www.cnblogs.com/haippy/p/3236136.html