生产者消费者模式

相关:互斥锁、条件变量、原子操作、windows临界区、线程同步、解耦、设计类

 BlockQueue使用了互斥和同步,来使队列中的元素达到平衡。

producer和consumer如果需要,要设计使用自己的互斥及同步。

//BlockQueue.h
#pragma once//为了防止头文件的重复引用
#include <iostream>
#include <queue>
#include <thread>
#include <mutex>
#include <atomic>
#include <condition_variable>

#define TASK_NUM 8

using namespace std;

class BlockQueue 
{
private:
	mutex mt_;	//互斥锁
	condition_variable cv_con_;	//条件变量,在使用处生效,用于线程间的同步和通信
	condition_variable cv_prod_;
	//在存储方面相当于一个变量,此处只能存储true和false
	atomic<bool> stopped_;	//模板类,用于实现线程安全的原子操作

	queue<int> tasks_;	//容器适配器,是一种机制(先进先出)
	const int capacity_;	//队列容量大小
private:
	bool stopped() {
		return stopped_.load();	//获取原子变量的当前值,读
	}
	bool empty() {
		return tasks_.size() == 0 ? true : false;	//队列中元素个数
	}
	bool full() {
		return tasks_.size() == capacity_ ? true : false;
	}
public:
	BlockQueue();
	~BlockQueue();
	void stop() {
		stopped_.store(true);//将给定的值存储到原子对象中,写
	}
	bool available() {
		return !stopped() || !empty();
	}
	void push(const int& data);
	void pop(int& data);
};
//BlockQueue.cpp
#include "BlockQueue.h"

BlockQueue::BlockQueue() : capacity_(TASK_NUM), stopped_(false)
{
}

BlockQueue::~BlockQueue()
{
	stop();
	cv_con_.notify_all();	//唤醒所有等待在条件变量上的线程
	cv_prod_.notify_all();
}

void BlockQueue::push(const int& data)
{
	//通过析构函数帮助mutex解锁,避免死锁发生https://blog.csdn.net/weixin_42127358/article/details/123507748
	//保护上锁后的代码,防止对资源的竞争。尽量只在公共资源前后
	unique_lock<mutex> lck(mt_);	//mutex为基本互斥锁类(访问保护),unique_lock提供互斥锁的管理
	while (full()) {
		cv_con_.notify_one();	//唤醒一个等待在条件变量上的线程
		cv_prod_.wait(lck);	//使当前线程挂起,直到唤醒
	}
	tasks_.push(data);
	cv_con_.notify_one();
}

void BlockQueue::pop(int& data)
{
	unique_lock<mutex> lck(mt_);
	while (empty()) {
		if (this->stopped())
			return;
		cv_prod_.notify_one();	//如果正在等待的线程多于一个,则唤醒的线程是不确定的;没有等待,则不执行任何操作
		cv_con_.wait(lck, [this]() {
			return this->stopped() || !this->empty();
			});	//参数为互斥锁和等待条件
	}
	data = tasks_.front();	//返回首元素或尾元素
	tasks_.pop();	//移除首元素
	cv_prod_.notify_one();
}
//main.cpp
#include <iostream>
#include <Windows.h>
#include "BlockQueue.h"

using namespace std;
//临界区,为访问临界资源的那段代码称(临界资源是一次仅允许一个进程使用的共享资源)
//相当于一个互斥锁,用于控制对临界区的访问
CRITICAL_SECTION cs;	

void consumer(BlockQueue* bq) {
	while (bq->available()) {
		int data = -1;
		bq->pop(data);
		EnterCriticalSection(&cs);	//标识一个临界区
		cout << "<" << this_thread::get_id() << ">:" << data << "comsumed.\n";	//获取线程标识符
		LeaveCriticalSection(&cs);	//释放一个临界区
	}
	cout << "[" << this_thread::get_id() << "]:" << "consumer is done.\n";
}

void producer(BlockQueue* bq, int start, int max_num) {
	int i = 0;
	while (i++ < max_num) {
		int data = i + start;
		bq->push(data);
		EnterCriticalSection(&cs);
		cout << "[" << this_thread::get_id() << "]:" << data << "produced.\n";	//获取线程标识符
		LeaveCriticalSection(&cs);
	}
	cout << "[" << this_thread::get_id() << "]:" << "producer is done.\n";
}

int main() {
	BlockQueue bqueue;
	InitializeCriticalSection(&cs);	//初始化CRITICAL_SECTION

	vector<thread> th_prods;
	const int num_prod = 3;
	for (int i = 0; i < num_prod; ++i) {
		//emplace_back允许在容器内部构造元素,此处后面三个形参,为第一个形参的参数
		//在尾部创建一个元素,使用函数指针作为线程的执行函数
		th_prods.emplace_back(producer, &bqueue, i * 100, num_prod * 10);	
	}

	vector<thread> th_cons;
	const int num_con = 3;
	for (int i = 0; i < num_con; ++i) {
		th_cons.emplace_back(consumer, &bqueue);
	}

	for (auto &t : th_prods) {
		t.join();	//等待线程结束
	}
	for (auto& t : th_cons) {
		t.join();
	}

	DeleteCriticalSection(&cs);	//删除临界区
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值