一个blocking queue是一个支持 take 和 put 的 queue。
take操作从队列中取出一个元素,如果队列为空,那么take会阻塞到直到队列中有可用元素。
put操作往队列里放入一个元素,如果队列是满,那么put会阻塞到队列有可用空间。
一个简单的blocking queue的实现,首先我们要有一个非常底层的容器,至于是vector还是deque甚至是list都无所谓,我们这里选择deque作为容器。
前面定义的blocking queue是有界的,也就是说元素有上限,所以才会出现满元素阻塞的情况。
我们先从简单的无界阻塞队列开始,默认这个队列可以一直存储元素。于是我们就不用去判断队列满元素的情况了。这个时候我们只需要一个条件变量来判断队列为空并且阻塞就行。大概框架如下。
template<typename T>
class BlockQueue{
std::deque<T> que;
std::condition_variable empty;
std::mutex mtx; // declared for unique_lock;
public:
T take();
void put(T elem);
int size();
}
那么核心的take和put要怎么实现呢?
首先修改队列的操作要加锁,并发编程的时候只要涉及到修改可变变量都必须要加锁,不巧put和take都是在修改队列。
这里使用unique_lock来加锁
std::unique_lock<std::mutex> lock{mtx};
两个操作中只有take操作会出现队列为空的情况,所以take必须判断队列为空,这里使用条件变量
empty.wait(lock, [this]{return !que.empty();});
于是我们的put和take操作就出来了
template<typename T>
T BlockQueue::take(){
std::unique_lock<std::mutex> lock{mtx};
empty.wait(lock, [this]{return !que.empty();});
auto res = que.front():
que.pop_front():
return res;
}
template<typename T>
void BlockQueue::put(T elem){
std::unique_lock<std::mutex> lock{mtx};
que.push_back(T elem);
}
网友评论