生产者消费者设计模式.docx
《生产者消费者设计模式.docx》由会员分享,可在线阅读,更多相关《生产者消费者设计模式.docx(18页珍藏版)》请在冰豆网上搜索。
![生产者消费者设计模式.docx](https://file1.bdocx.com/fileroot1/2023-2/8/4aa8fe28-0b76-4150-bc5f-af6fa54e4f9a/4aa8fe28-0b76-4150-bc5f-af6fa54e4f9a1.gif)
生产者消费者设计模式
基于C++11实现生产者消费者设计模式
本文将综合运用C++11中的新的基础设施(主要是多线程、锁、条件变量)来阐述一个经典问题——生产者消费者模式,并给出完整的解决方案。
生产者消费者问题是多线程并发中一个非常经典的问题,相信学过操作系统课程的同学都清楚这个问题的根源。
本文将就四种情况分析并介绍生产者和消费者问题,它们分别是:
单生产者-单消费者模式,单生产者-多消费者模式,多生产者-单消费者模式,多生产者-多消费者模式,我会给出四种情况下的C++11并发解决方案,如果文中出现了错误或者你对代码有异议,欢迎交流。
1单生产者-单消费者模式
顾名思义,单生产者-单消费者模式中只有一个生产者和一个消费者,生产者不停地往产品库中放入产品,消费者则从产品库中取走产品,产品库容积有限制,只能容纳一定数目的产品,如果生产者生产产品的速度过快,则需要等待消费者取走产品之后,产品库不为空才能继续往产品库中放置新的产品,相反,如果消费者取走产品的速度过快,则可能面临产品库中没有产品可使用的情况,此时需要等待生产者放入一个产品后,消费者才能继续工作。
C++11实现单生产者单消费者模式的代码如下:
#include
#include
#include
#include
#include
#include
staticconstintkItemRepositorySize=10;//Itembuffersize.
staticconstintkItemsToProduce=1000;//Howmanyitemsweplantoproduce.
structItemRepository{
intitem_buffer[kItemRepositorySize];//产品缓冲区,配合read_position和write_position模式环形队列.
size_tread_position;//消费者读取产品位置.
size_twrite_position;//生产者写入产品位置.
std:
:
mutexmtx;//互斥量,保护产品缓冲区
std:
:
condition_variablerepo_not_full;//条件变量,指示产品缓冲区不为满.
std:
:
condition_variablerepo_not_empty;//条件变量,指示产品缓冲区不为空.
}gItemRepository;//产品库全局变量,生产者和消费者操作该变量.
typedefstructItemRepositoryItemRepository;
voidProduceItem(ItemRepository*ir,intitem)
{
std:
:
unique_lock:
mutex>lock(ir->mtx);
while(((ir->write_position+1)%kItemRepositorySize)
==ir->read_position){//itembufferisfull,justwaithere.
std:
:
cout<<"Produceriswaitingforanemptyslot...\n";
(ir->repo_not_full).wait(lock);//生产者等待"产品库缓冲区不为满"这一条件发生.
}
(ir->item_buffer)[ir->write_position]=item;//写入产品.
(ir->write_position)++;//写入位置后移.
if(ir->write_position==kItemRepositorySize)//写入位置若是在队列最后则重新设置为初始位置.
ir->write_position=0;
(ir->repo_not_empty).notify_all();//通知消费者产品库不为空.
lock.unlock();//解锁.
}
intConsumeItem(ItemRepository*ir)
{
intdata;
std:
:
unique_lock:
mutex>lock(ir->mtx);
//itembufferisempty,justwaithere.
while(ir->write_position==ir->read_position){
std:
:
cout<<"Consumeriswaitingforitems...\n";
(ir->repo_not_empty).wait(lock);//消费者等待"产品库缓冲区不为空"这一条件发生.
}
data=(ir->item_buffer)[ir->read_position];//读取某一产品
(ir->read_position)++;//读取位置后移
if(ir->read_position>=kItemRepositorySize)//读取位置若移到最后,则重新置位.
ir->read_position=0;
(ir->repo_not_full).notify_all();//通知消费者产品库不为满.
lock.unlock();//解锁.
returndata;//返回产品.
}
voidProducerTask()//生产者任务
{
for(inti=1;i<=kItemsToProduce;++i){
//sleep
(1);
std:
:
cout<<"Producethe"<
:
endl;
ProduceItem(&gItemRepository,i);//循环生产kItemsToProduce个产品.
}
}
voidConsumerTask()//消费者任务
{
staticintcnt=0;
while
(1){
sleep
(1);
intitem=ConsumeItem(&gItemRepository);//消费一个产品.
std:
:
cout<<"Consumethe"<:
endl;
if(++cnt==kItemsToProduce)break;//如果产品消费个数为kItemsToProduce,则退出.
}
}
voidInitItemRepository(ItemRepository*ir)
{
ir->write_position=0;//初始化产品写入位置.
ir->read_position=0;//初始化产品读取位置.
}
intmain()
{
InitItemRepository(&gItemRepository);
std:
:
threadproducer(ProducerTask);//创建生产者线程.
std:
:
threadconsumer(ConsumerTask);//创建消费之线程.
producer.join();
consumer.join();
}
2单生产者-多消费者模式
与单生产者和单消费者模式不同的是,单生产者-多消费者模式中可以允许多个消费者同时从产品库中取走产品。
所以除了保护产品库在多个读写线程下互斥之外,还需要维护消费者取走产品的计数器,代码如下:
#include
#include
#include
#include
#include
#include
staticconstintkItemRepositorySize=4;//Itembuffersize.
staticconstintkItemsToProduce=10;//Howmanyitemsweplantoproduce.
structItemRepository{
intitem_buffer[kItemRepositorySize];
size_tread_position;
size_twrite_position;
size_titem_counter;
std:
:
mutexmtx;
std:
:
mutexitem_counter_mtx;
std:
:
condition_variablerepo_not_full;
std:
:
condition_variablerepo_not_empty;
}gItemRepository;
typedefstructItemRepositoryItemRepository;
voidProduceItem(ItemRepository*ir,intitem)
{
std:
:
unique_lock:
mutex>lock(ir->mtx);
while(((ir->write_position+1)%kItemRepositorySize)
==ir->read_position){//itembufferisfull,justwaithere.
std:
:
cout<<"Produceriswaitingforanemptyslot...\n";
(ir->repo_not_full).wait(lock);
}
(ir->item_buffer)[ir->write_position]=item;
(ir->write_position)++;
if(ir->write_position==kItemRepositorySize)
ir->write_position=0;
(ir->repo_not_empty).notify_all();
lock.unlock();
}
intConsumeItem(ItemRepository*ir)
{
intdata;
std:
:
unique_lock:
mutex>lock(ir->mtx);
//itembufferisempty,justwaithere.
while(ir->write_position==ir->read_position){
std:
:
cout<<"Consumeriswaitingforitems...\n";
(ir->repo_not_empty).wait(lock);
}
data=(ir->item_buffer)[ir->read_position];
(ir->read_position)++;
if(ir->read_position>=kItemRepositorySize)
ir->read_position=0;
(ir->repo_not_full).notify_all();
lock.unlock();
returndata;
}
voidProducerTask()
{
for(inti=1;i<=kItemsToProduce;++i){
//sleep
(1);
std:
:
cout<<"Producerthread"<:
this_thread:
:
get_id()
<<"producingthe"<
:
endl;
ProduceItem(&gItemRepository,i);
}
std:
:
cout<<"Producerthread"<:
this_thread:
:
get_id()
<<"isexiting..."<:
endl;
}
voidConsumerTask()
{
boolready_to_exit=false;
while
(1){
sleep
(1);
std:
:
unique_lock:
mutex>lock(gItemRepository.item_counter_mtx);
if(gItemRepository.item_counterintitem=ConsumeItem(&gItemRepository);
++(gItemRepository.item_counter);
std:
:
cout<<"Consumerthread"<:
this_thread:
:
get_id()
<<"isconsumingthe"<:
endl;
}elseready_to_exit=true;
lock.unlock();
if(ready_to_exit==true)break;
}
std:
:
cout<<"Consumerthread"<:
this_thread:
:
get_id()
<<"isexiting..."<:
endl;
}
voidInitItemRepository(ItemRepository*ir)
{
ir->write_position=0;
ir->read_position=0;
ir->item_counter=0;
}
intmain()
{
InitItemRepository(&gItemRepository);
std:
:
threadproducer(ProducerTask);
std:
:
threadconsumer1(ConsumerTask);
std:
:
threadconsumer2(ConsumerTask);
std:
:
threadconsumer3(ConsumerTask);
std:
:
threadconsumer4(ConsumerTask);
producer.join();
consumer1.join();
consumer2.join();
consumer3.join();
consumer4.join();
}
3多生产者-单消费者模式
与单生产者和单消费者模式不同的是,多生产者-单消费者模式中可以允许多个生产者同时向产品库中放入产品。
所以除了保护产品库在多个读写线程下互斥之外,还需要维护生产者放入产品的计数器,代码如下:
#include
#include
#include
#include
#include
#include
staticconstintkItemRepositorySize=4;//Itembuffersize.
staticconstintkItemsToProduce=10;//Howmanyitemsweplantoproduce.
structItemRepository{
intitem_buffer[kItemRepositorySize];
size_tread_position;
size_twrite_position;
size_titem_counter;
std:
:
mutexmtx;
std:
:
mutexitem_counter_mtx;
std:
:
condition_variablerepo_not_full;
std:
:
condition_variablerepo_not_empty;
}gItemRepository;
typedefstructItemRepositoryItemRepository;
voidProduceItem(ItemRepository*ir,intitem)
{
std:
:
unique_lock:
mutex>lock(ir->mtx);
while(((ir->write_position+1)%kItemRepositorySize)
==ir->read_position){//itembufferisfull,justwaithere.
std:
:
cout<<"Produceriswaitingforanemptyslot...\n";
(ir->repo_not_full).wait(lock);
}
(ir->item_buffer)[ir->write_position]=item;
(ir->write_position)++;
if(ir->write_position==kItemRepositorySize)
ir->write_position=0;
(ir->repo_not_empty).notify_all();
lock.unlock();
}
intConsumeItem(ItemRepository*ir)
{
intdata;
std:
:
unique_lock:
mutex>lock(ir->mtx);
//itembufferisempty,justwaithere.
while(ir->write_position==ir->read_position){
std:
:
cout<<"Consumeriswaitingforitems...\n";
(ir->repo_not_empty).wait(lock);
}
data=(ir->item_buffer)[ir->read_position];
(ir->read_position)++;
if(ir->read_position>=kItemRepositorySize)
ir->read_position=0;
(ir->repo_not_full).notify_all();
lock.unlock();
returndata;
}
voidProducerTask()
{
boolready_to_exit=false;
while
(1){
sleep
(1);
std:
:
unique_lock:
mutex>lock(gItemRepository.item_counter_mtx);
if(gItemRepository.item_counter++(gItemRepository.item_counter);
ProduceItem(&gItemRepository,gItemRepository.item_counter);
std:
:
cout<<"Producerthread"<:
this_thread:
:
get_id()
<<"isproducingthe"<<<"^thitem"<:
endl;
}elseready_to_exit=true;
lock.unlock();
if(ready_to_exit==true)break;
}
std:
:
cout<<"Producerthread"<:
this_thread:
:
get_id()
<<"isexiting..."<:
endl;
}
voidConsumerTask()
{
staticintitem_consumed=0;
while
(1){
sleep
(1);
++item_consumed;
if(item_consumed<=kItemsToProduce){
intitem=ConsumeItem(&gItemRepository);
std:
:
cout<<"Consumerthread"<:
this_thread:
:
get_id()
<<"isconsumingthe"<:
endl;
}elsebreak;
}
std:
:
cout<<"Consumerthread"<:
this_thread:
:
get_id()
<<"isexiting..."<:
endl;
}
voidInitItemRepository(ItemRepository*ir)
{
ir->write_position=0;
ir->read_position=0;
ir->item_counter=0;
}
intmain()
{
InitItemRepository(&gItemRepository);
std:
:
threadproducer1(ProducerTask);
std:
:
threadproducer2(ProducerTask);
std:
:
threadproducer3(ProducerTask);
std:
:
threadproducer4(ProducerTask);
std:
:
threadconsumer(ConsumerTask);
producer1.join();
producer2.join();
producer3.join();
producer4.join();
consumer.join();
}
4多生产者-多消费者模式
该模式可以说是前面两种模式的综合,程序需要维护两个计数器,分别是生产者已生产产品的数目和消费者已取走产品的数目。
另外也需要保护产品库在多个生产者和多个消费者互斥地访问。
代码如下:
#include
#include
#include
#include
#in