生产者消费者问题实现
C++实现生产者和消费者

C++实现⽣产者和消费者传统的⽣产者消费者模型⽣产者-消费者模式是⼀个⼗分经典的多线程并发协作的模式,弄懂⽣产者-消费者问题能够让我们对并发编程的理解加深。
所谓⽣产者-消费者问题,实际上主要是包含了两类线程,⼀种是⽣产者线程⽤于⽣产数据,另⼀种是消费者线程⽤于消费数据,为了解耦⽣产者和消费者的关系,通常会采⽤共享的数据区域,就像是⼀个仓库,⽣产者⽣产数据之后直接放置在共享数据区中,并不需要关⼼消费者的⾏为;⽽消费者只需要从共享数据区中去获取数据,就不再需要关⼼⽣产者的⾏为。
但是,这个共享数据区域中应该具备这样的线程间并发协作的功能:本⽂的⽣产者消费者模型但是本篇⽂章不是说的多线程问题,⽽是为了完成⼀个功能,设置⼀个⼤⼩固定的⼯⼚,⽣产者不断的往仓库⾥⾯⽣产数据,消费者从仓库⾥⾯消费数据,功能类似于⼀个队列,每⼀次⽣产者⽣产数据放到队尾,消费者从头部不断消费数据,如此循环处理相关业务。
代码下⾯是⼀个泛型的⼯⼚类,可以不断的⽣产数据,消费者不断的消费数据。
//// Created by muxuan on 2019/6/18.//#include <iostream>#include <vector>using namespace std;#ifndef LNRT_FACTORY_H#define LNRT_FACTORY_Htemplate<typename T>class Factory {private:vector<T> _factory;int _size = 5;bool logEnable = false;public:void produce(T item);T consume();void clear();void configure(int cap, bool log = false);};template<typename T>void Factory<T>::configure(int cap, bool log) {this->_size = cap;this->logEnable = log;}template<typename T>void Factory<T>::produce(T item) {if (this->_factory.size() < this->_size) {this->_factory.push_back(item);if (logEnable) cout << "produce product " << item << endl;return;}if (logEnable) cout << "consume product " << this->consume() << endl;this->_factory[this->_size - 1] = item;if (logEnable) cout << "produce product " << item << endl;}template<typename T>T Factory<T>::consume() {T item = this->_factory[0];for (int i = 1; i < this->_size; i++) this->_factory[i - 1] = this->_factory[i];return item;}template<typename T>void Factory<T>::clear() {for (int i = 0; i < this->_size; i++) if (logEnable) cout << "consume product " << this->consume() << endl;}#endif //LNRT_FACTORY_H测试Factory<int> factory;factory.configure(5,true);for (int i = 0; i < 10; ++i) {factory.produce(i);}factory.clear();⽤途该类可以很⽅便的实现分组问题,⽐如处理视频序列时候将第i帧到第j帧数据作为⼀个分组处理任务,可以⽤下⾯的⽅法来实现。
操作系统中的经典问题——生产者消费者问题(两种方式实现)

操作系统中的经典问题——⽣产者消费者问题(两种⽅式实现)操作系统中的经典问题——⽣产者消费者问题(两种⽅式实现)1、问题引⼊:什么是⽣产者消费者问题?⽣产者消费者问题(英语:Producer-consumer problem),也称有限缓冲问题(英语:Bounded-buffer problem),是⼀个多线程同步问题的经典案例。
该问题描述了共享固定⼤⼩缓冲区的两个线程——即所谓的“⽣产者”和“消费者”——在实际运⾏时会发⽣的问题。
⽣产者的主要作⽤是⽣成⼀定量的数据放到缓冲区中,然后重复此过程。
与此同时,消费者也在缓冲区消耗这些数据。
该问题的关键就是要保证⽣产者不会在缓冲区满时加⼊数据,消费者也不会在缓冲区中空时消耗数据。
.要解决该问题,就必须让⽣产者在缓冲区满时休眠(要么⼲脆就放弃数据),等到下次消费者消耗缓冲区中的数据的时候,⽣产者才能被唤醒,开始往缓冲区添加数据。
同样,也可以让消费者在缓冲区空时进⼊休眠,等到⽣产者往缓冲区添加数据之后,再唤醒消费者。
通常采⽤进程间通信的⽅法解决该问题。
如果解决⽅法不够完善,则容易出现死锁的情况。
出现死锁时,两个线程都会陷⼊休眠,等待对⽅唤醒⾃⼰。
该问题也能被推⼴到多个⽣产者和消费者的情形。
2、问题分析该问题需要注意的⼏点:1. 在缓冲区为空时,消费者不能再进⾏消费2. 在缓冲区为满时,⽣产者不能再进⾏⽣产3. 在⼀个线程进⾏⽣产或消费时,其余线程不能再进⾏⽣产或消费等操作,即保持线程间的同步4. 注意条件变量与互斥锁的顺序由于前两点原因,因此需要保持线程间的同步,即⼀个线程消费(或⽣产)完,其他线程才能进⾏竞争CPU,获得消费(或⽣产)的机会。
对于这⼀点,可以使⽤条件变量进⾏线程间的同步:⽣产者线程在product之前,需要wait直⾄获取⾃⼰所需的信号量之后,才会进⾏product的操作;同样,对于消费者线程,在consume之前需要wait直到没有线程在访问共享区(缓冲区),再进⾏consume的操作,之后再解锁并唤醒其他可⽤阻塞线程。
信号量实现生产者消费者问题

生产者——消费者问题一、实验目的通过实验,掌握Windows和Linux环境下互斥锁和信号量的实现方法,加深对临界区问题和进程同步机制的理解,同时巩固利用Windows API和Pthread API进行多线程编程的方法。
二、实验内容1. 在Windows操作系统上,利用Win32 API提供的信号量机制,编写应用程序实现生产者——消费者问题。
2. 在Linux操作系统上,利用Pthread API提供的信号量机制,编写应用程序实现生产者——消费者问题。
3. 两种环境下,生产者和消费者均作为独立线程,并通过empty、full、mutex三个信号量实现对缓冲进行插入与删除。
4. 通过打印缓冲区中的内容至屏幕,来验证应用程序的正确性。
三、实验步骤㈠设计思路①声明三个信号量,互斥信号量mutex,计数信号量empty初始化值100,技术信号量full初始化值0,并声明一个缓冲区char类型的buffer数组,大小为10,初始值为N。
②produce利用信号量形成对buffer临界生产,添加一个元素进去,赋值为A。
consume利用信号量形成对buffer临界消费,从buffer 中拿出来一个元素,并将buffer中原来元素赋为B。
每访问一次临界区,就输出buffer值。
③创建多线程,其中5个生产者,和5个消费者,每个线程最多操作缓冲区10次。
㈡流程图四、主要数据结构及其说明声明一个char类型的数组buffer,大小为10,用来做生产者和消费者共同访问的共享变量,可视做临界区。
五、程序运行时的初值和运行结果Win32下Linux下六、实验体会本实验总体感觉不是很难,就是将用信号量解决生产者消费者问题用具体的代码实现。
主要的工作就是分别在Win32和Pthread下用不同的函数名来实现P操作和V操作。
还有就是如何验证程序的正确性,就是通过没执行一次操作就输出缓冲区buffer值来判断是否符合要求。
七、源程序并附上注释Win32下#include <IOSTREAM.h>#include <STDIO.H>#include <windows.h>HANDLE Empty,full,mutex;//声明3个信号量,互斥信号量mutex,计数信号量full和Emptyint x=0;int y=0;char *buffer;//缓冲区buffer//输出buffervoid output(){for (int i=0;i<10;i++){cout<<buffer[i]<<" ";}cout<<"\n";}DWORD WINAPI produce(LPVOID param){int j=0;do{WaitForSingleObject(Empty,INFINITE);//buffer空余量减一WaitForSingleObject(mutex,INFINITE);//形成互斥,只能一个线程去生产cout<<GetCurrentThreadId()<<"^^^^^"<<j<<"^^^^^";//输出当前线程的id号,和执行的次数buffer[(y++%10)]='A';//生产赋值为Aoutput();//输出bufferj++;ReleaseSemaphore(mutex,1, NULL);//取消互斥,允许其他线程生产ReleaseSemaphore(full,1, NULL);//可以消费量加1 } while (j!=10);//每个线程生产10次return 0;}DWORD WINAPI consume(LPVOID param){int j=0;do{WaitForSingleObject(full,INFINITE);//将可以消费量减1WaitForSingleObject(mutex,INFINITE);//形成互斥访问,自能一个线程可以访问。
生产者消费者问题实验报告

操作系统课程设计实验报告实验名称: 生产者消费者问题姓名/学号:一、实验目的以生产者和消费者问题为例, 学习Linux和Windows下进程通信、同步机制的具体实现方法, 主要是信号量和共享内存。
熟悉相关系统API的用法。
二、实验内容使用共享内存和信号量机制来实现多个生产者/消费者进程间的通信和同步。
要求在Linux和Windows下分别实现。
缓冲区大小为3, 初始为空。
2个生产者, 随机等待一段时间, 往缓冲区添加数据, 重复6次。
3个消费者, 重复4次。
三、实验环境Ubuntu 10.10 , GCC; Windows 7, VC 6.0;四、程序设计与实现1.Linux下:(1) 数据结构:a.共享内存定义为一个结构, 使得其数据成员更清晰且操作变得简单。
b.共享缓冲区采用循环队列的数据结构,由上面的结构struct buf { int start; int end; int info[BUF_NUM]; }维护。
其中start为队头指针, end为队尾指针, info为数据区域。
(2) 算法:a.大致由三个模块组成:i.主程序(main):ii.创建信号量、共享内存并进行初始化iii.创建生产者、消费者进程, 生产者执行pro_fun(), 消费者执行con_fun()iv.等待所有子进程的结束v.删除信号量、共享内存i.生产者进程(pro_fun):ii.通过key获得信号量、共享内存的ID, 将内存添加到自己的地址空间iii.P(empty), P(mutex), Add(data), V(mutex), V(full)iv.解除和共享内存的关联i.消费者进程(con_fun):ii.通过key获得信号量、共享内存的ID, 将内存添加到自己的地址空间iii.P(full), P(mutex), Add(data), V(mutex), V(empty)iv.解除和共享内存的关联循环队列部分:加入数据: info[end] = value; end = (end + 1) % 3;取出数据: temp = info[start]; info[start] = 0; (start = start + 1)%3; return temp;(3) 程序流程图:a.主函数:b.生产者进程:c.消费者进程和生产者类似4.Windows 下:(1) 数据结构:和Linux大致相同(2) 算法:a.创建的子进程调用正在执行的文件本身, 通过main函数的参数区分主进程和生产者、消费者进程。
操作系统之生产者消费者问题(c++实现)

{
std::cout<<"* - - - - - - - - - - - - - - - - - - - - - - - *"<<std::endl;
std::cout<<"|课程设计课题:生产者-消费者问题的模拟实现|"<<std::endl;
std::cout<<"|指导老师:李先锋|"<<std::endl;
bool g_continue = 1;//控制程序运行:1表示继续运行?0表示停止运行
HANDLE g_hMutex;//线程间的互斥信号量
HANDLE g_hFullSemaphore;//资源信号量:缓冲区满
HANDLE g_hEmptySemaphore;//资源信号量:缓冲区空
DWORD WINAPI Producer(LPVOID);//生产者线程
std::cout<<"|学生:丁可|"<<std::endl;
std::cout<<"|班级: B计123班|"<<std::endl;
std::cout<<"* - - - - - - - - - - - - - - - - - - - - - - - *"<<std::endl;
std::cout<<" ==》按回车开始该程序"<<std::endl;
getchar();
}
/*----------------------------程序提示信息结束------------------------------*/
操作系统_生产者与消费者问题的实现

//shmctl(shmid,IPC_RMID,&buf);
//del_sem(semid);
return 0;
}
/*生产者写5次后退出*/
void producer(key_t keyFull, key_t keyEmpty)
empty Write P1 : 2
empty Write P2 : 4
in = 0
Write : 5
full Write V1 : 0
full Write V2 : 0
full Read P2 : 0
out = 0
Read : 5
empty Read V1 : 4
empty Read V2 : 5
Write : 7
full Write V1 : 0
full Write V2 : 0
full Read P2 : 0
out = 2
Read : 7
empty Read V1 : 4
empty Read V2 : 5
Read Finish
p(empty); /*是否有空缓冲区,有则占有,无则被挂起,是原子操作*/
printf("empty Write P2 : %d\n", semctl(empty, 0, GETVAL, 0));
value_write++;
in = in++;
pData[in] = value_write;
in = in % 5;
printf("full Write V2 : %d\n", semctl(full, 0, GETVAL, 0));
实习5生产者-消费者问题实现

一、实习内容
熟悉临界资源、信号量及PV操作的定义与物理意义了解进程通信的方法掌握进程互斥与进程同步的相关知识掌握用信号量机制解决进程之间的同步与互斥问题实现生产者-消费者问题,深刻理解进程同步问题
二、实习目的
在Linux操作系统下用C实现经典同步问题:生产者—消费者,具体要求如下: (1)一个大小为10的缓冲区,初始状态为空。 (2)2个生产者,随机等待一段时间,往缓冲区中添加数据,若缓冲区已满,等待消费者取走数据之后再添加,重复10次。 (3)2个消费者,随机等待一段时间,从缓冲区中读取数据,若缓冲区为空,等待生产者添加数据之后再读取,重复10次。
管道通信系统 向管道提供输入的发送进程,以字符流方式将大量的数据送入管道,而接收进程从管道中接收数据。由于发送进程和接收进程是利用管道进行通信的,故称为管道通信。 为了协调发送和接收双方的通信,管道通信机制必须提供以下3方面的协调功能。 (1)互斥 当一个进程正在对pipe文件进行读或写操作时,另一个进程必须等待。 (2)同步 当写进程把一定数量的数据写入pipe文件后,便阻塞等待,直到读进程取走数据后,再把写进程唤醒。 (3)确认对方是否存在 只有确定对方已存在时,才能进行管道通信,否则会造成因对方不存在而无限制地等待。
在这个问题当中,我们采用信号量机制进行进程之间的通信,设置两个信号量,空的信号量和满的信号量。 在Linux系统中,一个或多个信号量构成一个信号量集合。使用信号量机制可以实现进程之间的同步和互斥,允许并发进程一次对一组信号量进行相同或不同的操作。每个P、V操作不限于减1或加1,而是可以加减任何整数。在进程终止时,系统可根据需要自动消除所有被进程操作过的信号量的影响 1.缓冲区采用循环队列表示,利用头、尾指针来存放、读取数据,以及判断队列是否为空。缓冲区中数组大小为10; 2.利用随机函数rand()得到A~Z的一个随机字符,作为生产者每次生产的数据,存放到缓冲区中;
生产者消费者问题实验报告

生产者消费者问题实验报告生产者消费者问题实验报告一、引言生产者消费者问题是计算机科学中一个经典的并发问题,主要涉及到多个线程之间的协作和资源的共享。
在本实验中,我们通过编写一个简单的程序来模拟生产者和消费者之间的交互过程,以深入理解该问题的本质和解决方案。
二、问题描述在生产者消费者问题中,有两类线程:生产者和消费者。
生产者线程负责生产一定数量的产品,而消费者线程则负责消费这些产品。
两类线程需要共享一个有限的缓冲区,生产者将产品放入缓冲区,而消费者从缓冲区中取出产品。
然而,缓冲区的容量是有限的,当缓冲区已满时,生产者需要等待,直到有空间可用。
同样地,当缓冲区为空时,消费者需要等待,直到有产品可用。
三、实验设计为了解决生产者消费者问题,我们采用了经典的解决方案——使用互斥锁和条件变量。
互斥锁用于保护共享资源的访问,保证同一时间只有一个线程可以访问共享资源。
而条件变量用于线程之间的通信,当某个条件不满足时,线程可以通过条件变量进入等待状态,直到条件满足时再被唤醒。
在我们的程序中,我们使用了一个有界缓冲区来模拟生产者消费者之间的交互。
缓冲区的大小可以通过参数进行设置。
我们创建了两个线程分别代表生产者和消费者,它们通过互斥锁和条件变量来实现同步。
生产者线程在缓冲区未满时将产品放入缓冲区,并通知消费者线程有产品可用;消费者线程在缓冲区非空时从缓冲区取出产品,并通知生产者线程有空间可用。
通过这种方式,我们保证了生产者和消费者之间的协作和资源的共享。
四、实验结果经过多次运行实验,我们观察到了以下现象:当生产者线程的生产速度大于消费者线程的消费速度时,缓冲区会被生产者填满,消费者需要等待;当消费者线程的消费速度大于生产者线程的生产速度时,缓冲区会被消费者清空,生产者需要等待。
只有当生产者和消费者的速度相等时,才能实现平衡的生产和消费。
此外,我们还发现在某些情况下,生产者和消费者线程可能出现死锁或饥饿现象。
死锁是指两个或多个线程相互等待对方释放资源,导致程序无法继续执行的情况。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
生产者消费者问题实现
班级姓名学号
一.实验目的
1.熟悉临界资源、信号量及PV操作的定义与物理意义
2.了解线程通信的方法
3.掌握线程互斥与同步的相关知识
4.掌握用信号量机制解决线程之间的同步与互斥问题
5.实现生产者-消费者问题,深刻理解线程同步问题
二、实验的硬件、软件平台
硬件:计算机
软件:操作系统win10
应用软件:Dev C++
三、实验原理
生产者-消费者问题是一个经典的线程同步问题,该问题最早由Dijkstra提出,用以演示他提出的信号量机制。
在同一个线程地址空间内执行的两个线程。
生产者线程生产物品,然后将物品放置在一个空缓冲区中供消费者线程消费。
消费者线程从缓冲区中获得物品,然后释放缓冲区。
当生产者线程生产物品时,如果没有空缓冲区可用,那么生产者线程必须等待消费者线程释放出一个空缓冲区。
当消费者线程消费物品时,如果没有满的缓冲区,那么消费者线程将被阻塞,直到新的物品被生产出来。
多个生产/消费者在有界缓冲上操作。
它利用N个字节的共享内存作为有界循环缓冲区,利用写一字符模拟放一个产品,利用读一字符模拟消费一个产品。
当缓冲区空时消费者应阻塞睡眠,而当缓冲区满时生产者应当阻塞睡眠。
一旦缓冲区中有空单元,生产者线程就向空单元中入写字符,并报告写的内容和位置。
一旦缓冲区中有未读过的字符,消费者线程就从该单元中读出字符,并报告读取位置。
生产者不能向同一单元中连续写两次以上相同的字符,消费者也不能从同一单元中连续读两次以上相同的字符。
在进行多线程编程时,由于资源共享和进程间合作而造成进程间相互制约,难免还要碰到两个问题,那就线程间的互斥与同步:线程同步是指线程之间所具有的一种制约关系,一个线程的执行依赖另一个线程的消息,当它没有得到另一个线程的消息时应等待,直到消息到达时才被唤醒。
线程互斥是指对于共享的进程系统资源,在各单个线程访问时的排它性。
当有若干个线程都要使用某一共享资源时,任何时刻最多只允许一个线程去使用,其它要使用该资源的线程必须等待,直到占用资源者释放该资源。
线程互斥可以看成是一种特殊的线程同步(下文统称为同步)。
线程间的同步方法大体可分为两类:用户模式和内核模式。
内核模式就是指
利用系统内核对象的单一性来进行同步,使用时需要切换内核态与用户态,而用户模式就是不需要切换到内核态,只在用户态完成操作。
用户模式下的方法有:原子操作(例如一个单一的全局变量),临界区。
内核模式下的方法有:事件,信号量,互斥量。
在本实验中,线程之间要进行通信来操作同一缓冲区。
一般来说,线程间的通信可以划分为三种:1)使用全局变量实现线程间通信;2)使用消息实现线程间通信;3)使用CEvent类实现线程间通信。
四、实验内容及步骤
实验内容:模拟操作系统中进程同步和互斥。
实现经典同步问题:生产者—消费者,具体要求如下(可采用C++或者java)
(1)一个大小为10的缓冲区,初始状态为空。
(2)2个生产者,随机等待一段时间,往缓冲区中添加数据,若缓冲区已满,等
待消费者取走数据之后再添加,重复10次。
(3)2个消费者,随机等待一段时间,从缓冲区中读取数据,若缓冲区为空,等
待生产者添加数据之后再读取,重复10次。
(4)按要求输出。
实验步骤:
首先,使用一个互斥锁,意味着资源槽机制就不能使用了。
因为资源槽虽以用一个互斥锁完成,但是需要有额外的通信,如果使用管道通信,则管道也必须是互斥,这就不满足1个互斥锁的要求。
其次,要求生产者一直生产,这就否定了另外一种方法:消费者、生产者的位置均平等,消费者消费的时候生产者不能生产,生产者生产的时候消费者不能消费。
因此,就需要采用A要求,也就是循环链表的形式。
为了保证互斥要求,需要定义一个数据结构,这个数据结构包含两个指针,一个读一个写,同时有一个资源数目量,告诉生产者和消费者是否可以生产或者消费。
由于该数据结构很小,因而可以对此结构互斥访问。
同时,对于每组数据,都有一个标志位,表示此组数据是否被占用,生产者和消费者均可以先占用此位置然后完成相应的操作。
当消费者互斥访问此结构时,首先判断是否有数据可以取,如果没有,直接等待,若有数据可取,先更改标志位占用此数据,并将资源数目-1。
然后交出互斥,把数据拷贝到自己缓冲区内,清空数据。
当生产者访问时,首先判断有没有空位可以生产,如果没有,直接等待,若有数据可以生产,先判断该位是否被占用,如果没被占用,则占用此位置进行生产。
生产完成后,将占用位改为未占用,同时将资源数目+1。
流程图: A 、主函数
Y
Y N
Y N
B 、统计线程
Y
N
Main 函数开始 初始化公共数据区
初始化信号量 创建生产者进程 创建消费者进程
创建统计进程 成功?
成功?
成功?
定时到 退出
开始进程 定时时间+1 满屏 打印消费结束进程
修改退出
C 、生产者线程 N
Y
Y
N
Y
N
N
Y
Y
开始进程
要求退出
结束进程
加锁成功
有空位
取得成功
加锁成功
生产数据
解锁
设置取得成功
失败计数+1
设置取得失败
检查资源数目
要求加锁
解锁
增加资源数目
要求加锁
D 、消费者线程
N
Y
N
N
N
N
Y
Y
五、实现代码
六、运行结果
开始进程
要求退出
结束进程
加锁成功
有产品
取得成功 加锁成功
消费数据
解锁
设置取得成功
失败计数+1
设置取得失败
检查资源数目
要求加锁
解锁
增加资源数目
要求加锁
七、实验心得
通过这次实验,我对linux的线程、进程等知识有了更直观深入的了解。
通过写这几个程序,弄清楚了信号量及互斥在使用中的具体运行方法。
除了涉及多个知识点之外,自己在做程序的时候也通过解决问题,对linux下一些函数认识的更加透彻,例如对进程间的通信,多线程的安全问题,管道问题都有了进一步的了解。