cpp生产者消费者模型

前言

最近实习经常需要用到c++多线程编程,发现一个非常实用经典的多线程编程模型就是生产者消费者模型,这个模型是十分经典,能够帮助我们解决在多线程编程情况下遇到的很多的问题。

简单实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#pragma once
#include <condition_variable>
#include <mutex>
#include <queue>

template <typename T> class BlockingQueue {
public:
BlockingQueue<T>(const size_t &m_size)
: max_queue_length_(m_size), not_empty_(false), not_full_(true) {}
void Push(const T &ele);
T Pop();

private:
std::queue<T> elements_;
std::mutex elements_mutex_;
std::condition_variable not_full_cond_;
std::condition_variable not_empty_cond_;
bool not_full_;
bool not_empty_;
size_t max_queue_length_;
};

template <typename T> void BlockingQueue<T>::Push(const T &ele) {
std::unique_lock<std::mutex> lck(elements_mutex_);
if (elements_.size() < max_queue_length_) {
elements_.push(ele);
} else {
///需要等待队列内容被取出
not_full_ = false;
///为了避免假唤醒的情况出现
not_full_cond_.wait(lck, [this] { return not_full_; });
elements_.push(ele);
}
not_empty_ = true;
not_empty_cond_.notify_all();
}

template <typename T> T BlockingQueue<T>::Pop() {
std::unique_lock<std::mutex> lck(elements_mutex_);
T ele;
if (!elements_.empty()) {
ele = elements_.front();
elements_.pop();
} else {
not_empty_ = false;
not_empty_cond_.wait(lck, [this] { return not_empty_; });
ele = elements_.front();
elements_.pop();
}
not_full_ = true;
not_full_cond_.notify_all();
return ele;
}

解释

这里的思路其实也很简单,使用了条件变量和锁就实现了简单的一个类似于Java中BlockingQueue的模型,我在这里加上了一个队列的最大长度限制,为什么要加上这个东西呢,因为我自己在使用BlockingQueue的时候遇到一个问题,当时百思不得其解,简单来说,问题是这样的,消费者由于没注意进行了一个阻塞操作导致速度很慢,而生产者在某些情况下生产的速度很快,这就导致了队列无限制的增加,进而导致内存占用不断增加,这个当时我一直以为是内存泄漏的问题,当时找了很久也没找到,因此个人觉得最好需要加一下队列的最大长度,这样的话如果队列很快就满了,或者基本一直处在满的状态,基本上可以断定是消费的速度小于生产的速度,因此这个时候我们就需要仔细检查一下我们的设计,是否是有问题的,或者就是正常情况。