多生产者-单消费者模型中可以允许多个生产者同时向产品库中放入产品。所以除了保护产品库在多个读写线程下互斥之外,还需要维护生产者放入产品的计数器。
实际需求是地面站需要监控飞机上多个数据源插件的状态以及产生的数据量。地面站和飞机之间要通过链路进行周期性通信,只需要把相关状态按照协议预留字段填充到链路报文中,在地面站解析并显示即可。相关状态要在与地面站通信的进程或者线程中收集起来才能按照协议填充。
这里,每个数据源的状态需要统计ID、当前工作状态、已落盘数据量。多个数据源相当于多个生产者,而汇总这多个状态的相当于消费者。针对此问题,可以使用多生产者-单消费者模式来处理,具体的数据结构可以使用双端口dequeue,多个生产者从队列尾部填充数据,消费者从前端拿数据。
本文的程序是通过设置一起全局结构体用于存储临界区变量的。但是在我们的实际需求中,我们是在A进程里面的多个线程充当生产者,在A进程里面再启动一个线程充当消费者。然后再在消费者线程中创建一个管道或者跨进程消息队列,然后在与地面站交互的进程中读取管道或者队列中的消息,然后进行封装。
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <deque>
using namespace std;
static const int kItemsToProduce = 20;//定义生产者能够生产的最大产品个数
std::mutex stdoutMutex;//多线程标准输出 同步锁
struct ItemRepository
{
deque<int> itemQueue; // 这里用队列代表仓库缓冲区
int MaxSize = 10; // 仓库所容纳的产品最大个数
int itemCounter=0;
std::mutex mtx; // 互斥量,保护产品缓冲区
std::mutex itemCounterMtx;
std::condition_variable repository_notFull; // 条件变量, 指产品仓库缓冲区不为满
std::condition_variable repository_notEmpty; // 条件变量, 指产品仓库缓冲区不为空
}gItemRepository; // 产品库全局变量,生产者和消费者操作该变量.
typedef struct ItemRepository ItemRepository;
// 生产 产品
void ProduceItem(ItemRepository &itemRepo, int item)
{
std::unique_lock<std::mutex> lock(itemRepo.mtx);
itemRepo.repository_notFull.wait(lock, [&itemRepo] {
bool full = itemRepo.itemQueue.size() >= itemRepo.MaxSize;
if (full)
{
std::lock_guard<std::mutex> lock(stdoutMutex);
cout << "仓库满了,生产者等待中..." << "thread id = " << std::this_thread::get_id() << endl;
}
return !full;
});
itemRepo.itemQueue.push_back(item); // 仓库放入产品
itemRepo.repository_notEmpty.notify_all(); // 通知消费者仓库不为空
lock.unlock(); // 释放锁
}
// 消费 产品
int ConsumeItem(ItemRepository &itemRepo)
{
int data;
std::unique_lock<std::mutex> lock(itemRepo.mtx);
// 等待信号不为空的通知,wait 第二参数为true时 向下执行,否则一直等待
itemRepo.repository_notEmpty.wait(lock, [&itemRepo] {
bool empty = itemRepo.itemQueue.empty();
if (empty)
{
std::lock_guard<std::mutex> lock(stdoutMutex);
cout << "仓库空了,消费者等待中..." << "thread id = " << std::this_thread::get_id() << endl;
}
return !empty;
});
data = itemRepo.itemQueue.front();
itemRepo.itemQueue.pop_front();
itemRepo.repository_notFull.notify_all();
lock.unlock();
return data;
}
// 生产者任务
void ProducerTask(int th_ID)
{
bool readyToExit = false;
while (true)
{
//
this_thread::sleep_for(std::chrono::seconds(1));
std::unique_lock<std::mutex> lock(gItemRepository.itemCounterMtx); // 仓库产品消费计数器保持多线程互斥
if (gItemRepository.itemCounter < kItemsToProduce)
{
gItemRepository.itemCounter++;
ProduceItem(gItemRepository, gItemRepository.itemCounter); // 生产产品
{
std::lock_guard<std::mutex> lock(stdoutMutex);
cout << "Produce Thread " <<th_ID<<"the "<< gItemRepository.itemCounter << " ^th item..." << endl;
}
}
else
{
readyToExit = true;
}
lock.unlock();
if (readyToExit)
break;
}
{
std::lock_guard<std::mutex> lock(stdoutMutex);
cout << "Producer Thread "<<th_ID<<"exit.... " << endl;
}
}
// 消费者任务
void ConsumerTask()
{
static int item_consumed = 0;
while (true)
{
//this_thread::sleep_for(std::chrono::seconds(1));
item_consumed++;
if (item_consumed <= kItemsToProduce)
{
int item = ConsumeItem(gItemRepository); // 消费产品
{
std::lock_guard<std::mutex> lock(stdoutMutex);
cout << "Consume the " << item << "^th item..." << endl;
}
}
else
{
break;
}
}
{
std::lock_guard<std::mutex> lock(stdoutMutex);
cout << "Consumer Thread "<<" exit...." << endl;
}
}
int main()
{
std::thread producer1(ProducerTask,1);
std::thread producer2(ProducerTask,2);
std::thread producer3(ProducerTask,3);
std::thread producer4(ProducerTask,4);
std::thread consumer(ConsumerTask);
producer1.join();
producer2.join();
producer3.join();
producer4.join();
consumer.join();
system("pause");
return 0;
}
仓库空了,消费者等待中...thread id = 140157878253312
Produce Thread 3the 1 ^th item...
Produce Thread 2the 2 ^th item...
Produce Thread 4the 3 ^th item...
Consume the 1^th item...
Consume the 2^th item...
Consume the 3^th item...
仓库空了,消费者等待中...thread id = 140157878253312
Produce Thread 1the 4 ^th item...
Consume the 4^th item...
仓库空了,消费者等待中...thread id = 140157878253312
Produce Thread 2the 5 ^th item...
Produce Thread 3the 6 ^th item...
Produce Thread 4the 7 ^th item...
Consume the 5^th item...
Consume the 6^th item...
Consume the 7^th item...
仓库空了,消费者等待中...thread id = 140157878253312
Produce Thread 1the 8 ^th item...
Consume the 8^th item...
仓库空了,消费者等待中...thread id = 140157878253312
Produce Thread 3the 9 ^th item...
Produce Thread 2the 10 ^th item...
Produce Thread 4the 11 ^th item...
Consume the 9^th item...
Consume the 10^th item...
Consume the 11^th item...
仓库空了,消费者等待中...thread id = 140157878253312
Produce Thread 1the 12 ^th item...
Consume the 12^th item...
仓库空了,消费者等待中...thread id = 140157878253312
Produce Thread 2the 13 ^th item...
Produce Thread 3the 14 ^th item...
Produce Thread 4the 15 ^th item...
Consume the 13^th item...
Consume the 14^th item...
Consume the 15^th item...
仓库空了,消费者等待中...thread id = 140157878253312
Produce Thread 1the 16 ^th item...
Consume the 16^th item...
仓库空了,消费者等待中...thread id = 140157878253312
Produce Thread 3the 17 ^th item...
Produce Thread 2the 18 ^th item...
Produce Thread 4the 19 ^th item...
Consume the 17^th item...
Consume the 18^th item...
Consume the 19^th item...
仓库空了,消费者等待中...thread id = 140157878253312
Produce Thread 1the 20 ^th item...
Consume the 20^th item...
Consumer Thread exit....
Producer Thread 2exit....
Producer Thread 3exit....
Producer Thread 4exit....
Producer Thread 1exit....
参考链接:https://blog.csdn.net/zy13270867781/article/details/79231775