队列是在网络编程中是一个很重要的模块,很多地方都会使用到队列【例如线程池】;多个线程都可以对队列进行操作,从而带来同步问题,而解决队列同步的一个主要方式就是 生产者消费者模式
生产者消费者模式
处理生产者消费问题可以用 mutex + sem 或者 mutex + condition;muduo 库中使用的是 mutex + condition,所以我就使用一个简单的模型来讲解这个问题
//没有上界的生产者消费者
queue<int> q; //队列,FIFO
mutex_t mutex;
condition_t cond;
//消费者
void consumer() {
mutex.lock();
while(!q.empty()) {
//等待队列中有元素
cond.wait();
}
int tmp = q.front();
q.pop();
cout << tmp << endl;
mutex.unlock();
}
//生产者
void productor(int num) {
mutex.lock();
q.push(num);
//唤醒等待的线程
cond.signal();
//cond.boardcast
mutex.unlock();
}
-----------------------------------------------------------------
//有上界的生产者消费者模型
queue<int> q; //队列,FIFO
mutex_t mutex;
condition_t empty_cond;
condition_t full_cond;
int maxSize = 100;
//消费者
void consumer() {
mutex.lock();
while(q.empty()) {
//等待队列中有元素
empty_cond.wait();
}
int tmp = q.front();
q.pop();
cout << tmp << endl;
//由于取出一个元素了,此时队列不可能满了,唤醒等待不满的线程
full_cond.signal();
//full_cond.boardcast();
mutex.unlock();
}
//生产者
void productor(int num) {
mutex.lock();
while(q.size() == maxSize) {
full_cond.wait();
}
q.push(num);
//唤醒等待的线程
cond.signal();
//cond.boardcast
mutex.unlock();
}
- 网上有对生产者消费者模式的讲解,我这里只是简单的提一下,感兴趣可以自己去看看;
- 如果你对这个有了解的话,这个队列是很容易理解和实现的;这里是通过加锁的方式实现的,网上也有通过使用原子操作实现的队列,这个效率很更高,可以去了解一下;
- 有界和无界的区别就是多了一个条件变量,多了一个等待条件和唤醒;
BlockingQueue 的具体代码
template<typename T>
class BlockingQueue : noncopyable
{
public:
using queue_type = std::deque<T>;
BlockingQueue()
: mutex_(),
notEmpty_(mutex_),
queue_()
{
}
//生产者
void put(const T& x)
{
//加锁
MutexLockGuard lock(mutex_);
queue_.push_back(x);
notEmpty_.notify();
//函数执行完后自动解锁
}
//使用右值方式的生产者
void put(T&& x)
{
MutexLockGuard lock(mutex_);
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();
return front;
//结束后自动解锁
}
//返回队列长度
size_t size() const
{
MutexLockGuard lock(mutex_);
return queue_.size();
}
private:
mutable MutexLock mutex_; //一个互斥量
Condition notEmpty_ GUARDED_BY(mutex_); //一个条件变量
queue_type queue_ GUARDED_BY(mutex_); //模板队列,使用的是 STL 中的 deque
}; // __attribute__ ((aligned (64)));
注意:上面的锁都是封装过的,详细可以去看 mutex.h 和 condition.h,不是很难;
BoundedBlockingQueue 的具体实现
template<typename T>
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();
}
//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<T> queue_ GUARDED_BY(mutex_); //环形缓冲区
};