C++11实现生产者消费者模型

C++11实现生产者消费者模型

  • 生产者消费者模型是什么
    简单地举个例子,一个线程(或者进程)在往一个区域(可以使内存或其他地方)写数据,而同时有另外一个线程在取数据,这就存在了一个问题,存储的区域有限,数据存满了写数据的线程怎么办,还有数据空了,取数据的线程怎么处理,这时候就需要该模型来处理了。
  • 原理
    存储的数据的区域可以用一个队列来模拟,然后我们通过C++11提供的条件变量来通知写入线程在数据不满的时候进行写入,在数据满的时候挂起,对读取线程也是同理。
  • 需要C++11前置知识
    此次需要用到std::mutex互斥锁,用来防止写入与读取线程竞争数据区域。搭配使用std::unique_lock,利用RAII来控制锁释放,也是因为条件变量的存在需要该类型。
    当然还需要最重要的一个std::condition_variable,条件变量。介绍一下几个重要的函数。
  • wait()成员函数
void wait( std::unique_lock<std::mutex>& lock );
//Predicate 谓词函数,可以普通函数或者lambda表达式
template< class Predicate >
void wait( std::unique_lock<std::mutex>& lock, Predicate pred );

wait函数会阻塞当前线程直到条件变量被通知,或者被操作系统虚假唤醒(系统Bug),可选地循环知道满足某谓词

  • notify成员函数
void notify_one() noexcept;
若任何线程在 *this 上等待,则调用 notify_one 会解阻塞(唤醒)等待线程之一。

void notify_all() noexcept;
若任何线程在 *this 上等待,则解阻塞(唤醒)全部等待线程。
  • 实现代码
#include <mutex>
#include <condition_variable>
#include <deque>
#include <vector>
#include <iostream>
#include <thread>

std::mutex g_mtxDegue;
std::mutex g_mtxCout;
std::condition_variable g_cv_not_empty;
std::condition_variable g_cv_not_full;
std::deque<int> g_deque;
int g_itemIndex = 0;

const int g_kItemSize = 30;
const int g_kDequeSize = 10;

void produceItem()
{
	std::this_thread::sleep_for(std::chrono::seconds(1));
	std::unique_lock<std::mutex> lock(g_mtxDegue);
	g_cv_not_full.wait(lock, []() { return g_deque.size() < g_kDequeSize; });

	++g_itemIndex;
	g_deque.push_back(g_itemIndex);
	{
		std::lock_guard<std::mutex> lock_guard(g_mtxCout);
		std::cout << "produce item " << g_itemIndex << std::endl;
	}
	lock.unlock();
	g_cv_not_empty.notify_all();
}

void consumeItem()
{
	std::this_thread::sleep_for(std::chrono::seconds(2));
	std::unique_lock<std::mutex> lock(g_mtxDegue);
	g_cv_not_empty.wait(lock, []() { return !g_deque.empty(); });

	int itemIndex = g_deque.front();
	g_deque.pop_front();
	{
		std::lock_guard<std::mutex> lock_guard(g_mtxCout);
		std::cout << "\tconsume item " << itemIndex << std::endl;
	}
	lock.unlock();
	g_cv_not_full.notify_one();
}

void produceTask()
{
	int count = g_kItemSize;
	while (count--)
	{
		produceItem();
	}
}

void consumeTask()
{
	int count = g_kItemSize;
	while (count--)
	{
		consumeItem();
	}
}

//单生产者单消费者模型
void consumeProduceTest()
{
	std::vector<std::thread> threads;
	threads.push_back(std::thread(produceTask));
	threads.push_back(std::thread(consumeTask));
	for (int i = 0; i < 2; i++)
		threads.at(i).join();
}

这里只是简单地给出了单个生产者和单个消费者的示例,对于单对多、多对单和多对多,需要考虑的是生产者间或者消费者之间对数据数量这个值的互斥。

  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
生产者消费者问题是一个经典的多线程同步问题,可以使用C++中的互斥锁和条件变量来实现。 以下是一个简单的生产者消费者问题的C++代码示例: ```cpp #include <iostream> #include <queue> #include <thread> #include <mutex> #include <condition_variable> using namespace std; queue<int> q; // 生产者消费者队列 mutex m; // 互斥锁 condition_variable cv; // 条件变量 const int MAX_SIZE = 10; // 队列最大容量 int product_id = 0; // 生产者生产的产品编号 // 生产者线程函数 void producer() { while (true) { unique_lock<mutex> lock(m); // 如果队列已满,则等待 cv.wait(lock, []() { return q.size() < MAX_SIZE; }); // 生产产品 q.push(++product_id); cout << "Producer produces product " << product_id << endl; // 通知消费者队列中有产品可用 cv.notify_all(); lock.unlock(); // 生产完一个产品后休眠 this_thread::sleep_for(chrono::milliseconds(500)); } } // 消费者线程函数 void consumer() { while (true) { unique_lock<mutex> lock(m); // 如果队列为空,则等待 cv.wait(lock, []() { return !q.empty(); }); // 消费产品 int product = q.front(); q.pop(); cout << "Consumer consumes product " << product << endl; // 通知生产者队列中有空位 cv.notify_all(); lock.unlock(); // 消费完一个产品后休眠 this_thread::sleep_for(chrono::milliseconds(1000)); } } int main() { thread producer_thread(producer); thread consumer_thread(consumer); producer_thread.join(); consumer_thread.join(); return 0; } ``` 在上面的代码中,我们定义了一个生产者消费者队列 `q`,一个互斥锁 `m` 和一个条件变量 `cv`。生产者线程函数 `producer` 不断地生产产品,并将其加入队列中,如果队列已满,则等待条件变量 `cv`。消费者线程函数 `consumer` 不断地消费产品,并将其从队列中移除,如果队列为空,则等待条件变量 `cv`。 在生产者线程函数中,我们使用了 `unique_lock` 对互斥锁进行加锁,然后调用 `cv.wait` 等待条件变量。在等待期间,该线程会释放互斥锁,等待其他线程调用 `notify_all` 函数通知该线程条件变量的状态发生了变化。当条件变量的状态发生变化时,线程会重新获取互斥锁并继续执行。 在消费者线程函数中,我们也使用了 `unique_lock` 对互斥锁进行加锁,并调用 `cv.wait` 等待条件变量。当队列不为空时,线程从队列中取出一个产品并进行消费,并使用 `cv.notify_all` 函数通知生产者线程队列中有空位。 在主函数中,我们创建了两个线程分别执行生产者线程函数和消费者线程函数,并等待两个线程执行完毕后退出程序。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值