C++ マルチスレッド学習 (10、プロデューサー コンシューマー モード)

目次

生産者消費者のパターン

簡単な実装手順:

コード[含意コメント]:

コードコメント:

if (FW.ConsumePersonNum== WarehouseSize):

if (FW.ProductPersonPosition== ProductNum):


生産者消費者のパターン

プロデューサ/コンシューマ モードは、一般的に使用されるマルチスレッド プログラミング モードで、プロデューサとコンシューマ間のデータ対話の問題を解決するために使用されます。このパターンでは、プロデューサーはデータの生成を担当し、コンシューマーはデータの処理を担当します。マルチスレッドを通じて、プロデューサーはデータを共有バッファーに置き、コンシューマーは処理のためにバッファーからデータを取得します。

簡単な実装手順:

1. プロデューサによって生成されたデータを保存するための共有バッファを作成します。バッファーには、配列、キュー、またはその他のデータ構造を使用できます。

2. バッファへのアクセスを保護するミューテックスを作成します。ミューテックスは、一度に 1 つのスレッドだけがバッファにアクセスできるようにするために使用されます。

3. プロデューサがデータを生成し、コンシューマがデータを処理する間の通信のための条件変数 (条件変数) を作成します。条件変数は、特定の条件が満たされるまでスレッドをブロックするために使用されます。

4. データを生成するプロデューサー スレッドを作成します。プロデューサ スレッドは、ミューテックスを使用してバッファへのアクセスを保護し、データの生成時に条件変数を通じてコン​​シューマ スレッドに通知します。

5. データを処理するための 1 つ以上のコンシューマ スレッドを作成します。コンシューマ スレッドは、ミューテックスを使用してバッファへのアクセスを保護し、バッファが空になったときに条件変数を介して通知されるのを待ちます。

6. プロデューサ スレッドはデータを生成した後、ミューテックスを取得し、データをバッファに置きます。その後、1 つ以上のコンシューマ スレッドに条件変数を介して通知されます。

7. データを処理する前に、コンシューマ スレッドはミューテックスを取得し、バッファからデータを取得します。バッファが空の場合は、条件変数を介した通知を待ちます。

8. プロデューサ スレッドがデータを生成し、バッファにデータを配置した後、他のスレッドがバッファにアクセスできるようにミューテックスを解放する必要があります。

9. コンシューマ スレッドがデータの処理を終了したら、他のスレッドがバッファにアクセスできるようにミューテックスを解放する必要があります。

ミューテックスと条件変数を使用することで、スレッド間の同期と通信を実現し、プロデューサーとコンシューマー間の正しい対話を保証できます。

コード[含意コメント]:

#include <iostream>
#include <thread>
#include <deque>//双向队列
#include <mutex>
#include<condition_variable>
using namespace std;
const int ProductNum = 100;//产品个数
const int WarehouseSize = 10;//仓库大小
//仓库类
template<class T>
class Warehouse
{
public:
	Warehouse()
	{
		size_t ProductPersonNum = 0;
		size_t ConsumePersonNum = 0;
		size_t ProductPersonPosition = 0;
		size_t ConsumePersonPosition = 0;
	};
public:
	deque<T> SaveProduct;	//存储产品
	mutex mtx;				//生产者和消费者的互斥量
	mutex mtx_Product;		//生产计数互斥量
	mutex mtx_Consume;		//消费计数互斥量
	condition_variable Warehouse_noFull;	//条件变量:仓库没满
	condition_variable Warehouse_noEmpty;	//条件变量:仓库不是空的

	size_t ProductPersonNum;//生产者计数
	size_t ConsumePersonNum;//消费者计数

	//确定当前是哪个位置的生产者或消费者需要进行操作。
	size_t ProductPersonPosition;//生产者位置
	size_t ConsumePersonPosition;//消费者位置
};

//工厂类
template<class T>
class Factory
{
public:
	//任务派发,具体的实现在protected里面
	//生产者操作
	void ProducterTask()
	{
		bool bReadyExit = false;
		while (true)
		{
			unique_lock<mutex> lock(FactoryWarehouse.mtx_Product);//加锁产品互斥量
			//线程结束条件
			if (FactoryWarehouse.ProductPersonNum< ProductNum)//小于就继续做计数过程
			{
				FactoryWarehouse.ProductPersonNum++;
				//生产产品
				//this_thread::sleep_for(1s);//假设要1s生产产品
				T item = FactoryWarehouse.ProductPersonNum;
				cout << "生产者的ID:" << this_thread::get_id() << endl;
				cout << "货源号:" << item << endl;
				InputFWarehouse(FactoryWarehouse, item);
			}
			else//否则
			{
				bReadyExit = true;
			}
			lock.unlock();
			if (bReadyExit)
			{
				break;
			}
		}
	}
	//消费者操作
	void ConsumerTask()
	{
		bool bReadyExit = false;
		while (true)
		{
			unique_lock<mutex> lock(FactoryWarehouse.mtx_Consume);//加锁消费者互斥量
			if (FactoryWarehouse.ConsumePersonNum < ProductNum)
			{
				T item = GetProductInFWarehouse(FactoryWarehouse);
				//消费产品
				//this_thread::sleep_for(1s);
				cout << "消费者的ID:" << this_thread::get_id() << endl;
				cout << "消费的货源号:" << item << endl;
				FactoryWarehouse.ConsumePersonNum++;
			}
			else
			{
				bReadyExit = true;
			}
			lock.unlock();
			if (bReadyExit)
			{
				break;
			}
		}
	}
protected:
	Warehouse<T> FactoryWarehouse;//工厂仓库
	//把产品放到仓库里面
	void InputFWarehouse(Warehouse<T>& FW ,T item)
	{
		unique_lock<mutex> lock(FW.mtx);//加锁
		FW.SaveProduct.push_back(item);
		//当ProductPersonPosition达到了ProductNum时,将ConsumePersonPosition重置为0。这样可以保证循环使用消费者线程来消费产品。
		if (FW.ProductPersonPosition== ProductNum)									
		{
			FW.ConsumePersonPosition = 0;
		}
		FW.Warehouse_noEmpty.notify_all();//唤醒所有线程
	}
	//从仓库中取出产品
	T GetProductInFWarehouse(Warehouse<T>& FW)
	{
		unique_lock<mutex> lock(FW.mtx);
		while (FW.SaveProduct.empty())
		{
			cout << "无货物,请等待." << endl;
			FW.Warehouse_noEmpty.wait(lock);//没有货源,进行等待
		}
		T Data = FW.SaveProduct.front();
		FW.SaveProduct.pop_front();
		if (FW.ConsumePersonNum== WarehouseSize)
		{
			FW.ConsumePersonPosition = 0;
		}
		FW.Warehouse_noFull.notify_all();
		lock.unlock();
		return Data;
	}
};


int main()
{
	//测试
	cout << "主线程ID:" << this_thread::get_id() << endl;
	Factory<int> myFactory;
	//4个生产者
	thread Producter1(&Factory<int>::ProducterTask, &myFactory);
	thread Producter2(&Factory<int>::ProducterTask, &myFactory);
	thread Producter3(&Factory<int>::ProducterTask, &myFactory);
	thread Producter4(&Factory<int>::ProducterTask, &myFactory);
	//5个消费者
	thread Consumer1(&Factory<int>::ConsumerTask, &myFactory);
	thread Consumer2(&Factory<int>::ConsumerTask, &myFactory);
	thread Consumer3(&Factory<int>::ConsumerTask, &myFactory);
	thread Consumer4(&Factory<int>::ConsumerTask, &myFactory);
	thread Consumer5(&Factory<int>::ConsumerTask, &myFactory);

	Producter1.join();
	Producter2.join();
	Producter3.join();
	Producter4.join();
	Consumer1.join();
	Consumer2.join();
	Consumer3.join();
	Consumer4.join();
	Consumer5.join();
	return 0;
}

コードコメント:

if (FW.ConsumePersonNum== WarehouseSize):

コンシューマ スレッドの数がウェアハウスのサイズに達すると、すべてのコンシューマ スレッドが使い果たされたことになり、カウントを再開する必要があります。つまり、ConsumePersonPosition は 0 にリセットされ、次のコンシューマ スレッドは最初からそれを使用し始めます。

if (FW.ProductPersonPosition== ProductNum):

プロデューサ スレッドの数が製品の数に達すると、すべての製品が生成されたことを意味するため、カウントを再開する必要があります。つまり、ConsumePersonPosition は 0 にリセットされ、次のプロデューサー スレッドが最初から生産を開始します。

この目的は、プロデューサおよびコンシューマのスレッドをリサイクルする効果を達成することであり、これにより、各プロデューサおよびコンシューマのスレッドがリサイクルされ、境界に到達したために動作が停止することがなくなります。

おすすめ

転載: blog.csdn.net/q244645787/article/details/131610193