生产者消费者问题实现
实验三 生产者-消费者问题

实验三生产者-消费者问题的实现实验类型验证性实验实验目的通过生产者-消费者问题的模拟,加深对进程同步、共享存储器通信的理解。
实验要求实现生产者消费者问题模拟,显示每次添加和读取数据时缓冲区的状态,生产者和消费者用进程模拟,缓冲区用共享内存来实现。
一个大小为3的缓冲区,初始为空;2个生产者:随机等待一段时间,往缓冲区添加数据,若缓冲区已满,等待消费者取走数据后再添加,重复6次。
3个消费者:随机等待一段时间,从缓冲区读取数据,若缓冲区为空,等待生产者添加数据后再读取,重复4次。
实验指导一、共享存储区通信1、共享存储区机制的概念共享存储区(Share Memory)是UNIX系统中通信速度最高的一种通信机制。
该机制可使若干进程共享主存中的某一个区域,且使该区域出现(映射)在多个进程的虚地址空间中。
另一方面,一个进程的虚地址空间中又可连接多个共享存储区,每个共享存储区都有自己的名字。
当进程间欲利用共享存储区进行通信时,必须先在主存中建立一共享存储区,然后将它附接到自己的虚地址空间上。
此后,进程对该区的访问操作,与对其虚地址空间的其它部分的操作完全相同。
进程之间便可通过对共享存储区中数据的读、写来进行直接通信。
应当指出,共享存储区机制只为进程提供了用于实现通信的共享存储区和对共享存储区进行操作的手段,然而并未提供对该区进行互斥访问及进程同步的措施。
因而当用户需要使用该机制时,必须自己设置同步和互斥措施才能保证实现正确的通信。
2、涉及的系统调用(1)shmget( )创建、获得一个共享存储区。
系统调用格式:shmid=shmget(key,size,flag)该函数使用头文件如下:#include<sys/types.h>#include<sys/ipc.h>#include<sys/shm.h>参数定义int shmget(key,size,flag);key_t key;int size,flag;其中:key是共享存储区的名字;(唯一标识共享内存的键值。
生产者消费者问题代码

生产者消费者问题用vc++实现悬最佳答案#include<windows.h>#include<fstream.h>#include<stdio.h>#include<string>#include<conio.h>//定义一些常量;//本程序允许的最大临界区数;#define MAX_BUFFER_NUM 10//秒到微秒的乘法因子;#define INTE_PER_SEC 1000//本程序允许的生产和消费线程的总数;#define MAX_THREAD_NUM 64//定义一个结构,记录在测试文件中指定的每一个线程的参数struct ThreadInfo{int serial; //线程序列号char entity; //是P还是Cdouble delay; //线程延迟int thread_request[MAX_THREAD_NUM]; //线程请求队列int n_request; //请求个数};//全局变量的定义//临界区对象的声明,用于管理缓冲区的互斥访问;CRITICAL_SECTION PC_Critical[MAX_BUFFER_NUM];int Buffer_Critical[MAX_BUFFER_NUM]; //缓冲区声明,用于存放产品;HANDLE h_Thread[MAX_THREAD_NUM]; //用于存储每个线程句柄的数组;ThreadInfo Thread_Info[MAX_THREAD_NUM]; //线程信息数组;HANDLE empty_semaphore; //一个信号量;HANDLE h_mutex; //一个互斥量;DWORD n_Thread = 0; //实际的线程的数目;DWORD n_Buffer_or_Critical; //实际的缓冲区或者临界区的数目;HANDLE h_Semaphore[MAX_THREAD_NUM]; //生产者允许消费者开始消费的信号量;//生产消费及辅助函数的声明void Produce(void *p);void Consume(void *p);bool IfInOtherRequest(int);int FindProducePositon();int FindBufferPosition(int);int main(void){//声明所需变量;DWORD wait_for_all;ifstream inFile;//初始化缓冲区;for(int i=0;i< MAX_BUFFER_NUM;i++)Buffer_Critical[i] = -1;//初始化每个线程的请求队列;for(int j=0;j<MAX_THREAD_NUM;j++){for(int k=0;k<MAX_THREAD_NUM;k++)Thread_Info[j].thread_request[k] = -1;Thread_Info[j].n_request = 0;}//初始化临界区;for(i =0;i< MAX_BUFFER_NUM;i++)InitializeCriticalSection(&PC_Critical[i]);//打开输入文件,按照规定的格式提取线程等信息;inFile.open("test.txt");//从文件中获得实际的缓冲区的数目;inFile >> n_Buffer_or_Critical;inFile.get();printf("输入文件是:\n");//回显获得的缓冲区的数目信息;printf("%d \n",(int) n_Buffer_or_Critical);//提取每个线程的信息到相应数据结构中;while(inFile){inFile >> Thread_Info[n_Thread].serial;inFile >> Thread_Info[n_Thread].entity;inFile >> Thread_Info[n_Thread].delay;char c;inFile.get(c);while(c!='\n'&& !inFile.eof()){inFile>>Thread_Info[n_Thread].thread_request[Thread_Info[n_Thread].n_request++];inFile.get(c);}n_Thread++;}//回显获得的线程信息,便于确认正确性;for(j=0;j<(int) n_Thread;j++){int Temp_serial = Thread_Info[j].serial;char Temp_entity = Thread_Info[j].entity;double Temp_delay = Thread_Info[j].delay;printf(" \n thread%2d %c %f",Temp_serial,Temp_entity,Temp_delay);int Temp_request = Thread_Info[j].n_request;for(int k=0;k<Temp_request;k++)printf(" %d ", Thread_Info[j].thread_request[k]);cout<<endl;}printf("\n\n");//创建在模拟过程中几个必要的信号量empty_semaphore=CreateSemaphore(NULL,n_Buffer_or_Critical,n_Buffer_or _Critical,"semaphore_for_empty");h_mutex = CreateMutex(NULL,FALSE,"mutex_for_update");//下面这个循环用线程的ID号来为相应生产线程的产品读写时所//使用的同步信号量命名;for(j=0;j<(int)n_Thread;j++){std::string lp ="semaphore_for_produce_";int temp =j;while(temp){char c = (char)(temp%10);lp+=c;temp/=10;}h_Semaphore[j+1]=CreateSemaphore(NULL,0,n_Thread,lp.c_str());}//创建生产者和消费者线程;for(i =0;i< (int) n_Thread;i++){if(Thread_Info[i].entity =='P')h_Thread[i]=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)(Produce), &(Thread_Info[i]),0,NULL);elseh_Thread[i]=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)(Consu me),&(Thread_Info[i]),0,NULL);}//主程序等待各个线程的动作结束;wait_for_all = WaitForMultipleObjects(n_Thread,h_Thread,TRUE,-1); printf(" \n \nALL Producer and consumer have finished their work. \n"); printf("Press any key to quit!\n");_getch();return 0;}//确认是否还有对同一产品的消费请求未执行;bool IfInOtherRequest(int req){for(int i=0;i<n_Thread;i++)for(int j=0;j<Thread_Info[i].n_request;j++)if(Thread_Info[i].thread_request[j] == req)return TRUE;return FALSE;}//找出当前可以进行产品生产的空缓冲区位置;int FindProducePosition(){int EmptyPosition;for (int i =0;i<n_Buffer_or_Critical;i++)if(Buffer_Critical[i] == -1){EmptyPosition = i;//用下面这个特殊值表示本缓冲区正处于被写状态;Buffer_Critical[i] = -2;break;}return EmptyPosition;}//找出当前所需生产者生产的产品的位置;int FindBufferPosition(int ProPos){int TempPos;for (int i =0 ;i<n_Buffer_or_Critical;i++)if(Buffer_Critical[i]==ProPos){TempPos = i;break;}return TempPos;}//生产者进程void Produce(void *p){//局部变量声明;DWORD wait_for_semaphore,wait_for_mutex,m_delay;int m_serial;//获得本线程的信息;m_serial = ((ThreadInfo*)(p))->serial;m_delay = (DWORD)(((ThreadInfo*)(p))->delay *INTE_PER_SEC);Sleep(m_delay);//开始请求生产printf("Producer %2d sends the produce require.\n",m_serial);//确认有空缓冲区可供生产,同时将空位置数empty减1;用于生产者和消费者的同步;wait_for_semaphore = WaitForSingleObject(empty_semaphore,-1);//互斥访问下一个可用于生产的空临界区,实现写写互斥;wait_for_mutex = WaitForSingleObject(h_mutex,-1);int ProducePos = FindProducePosition();ReleaseMutex(h_mutex);//生产者在获得自己的空位置并做上标记后,以下的写操作在生产者之间可以并发;//核心生产步骤中,程序将生产者的ID作为产品编号放入,方便消费者识别; printf("Producer %2d begin to produce atposition %2d.\n",m_serial,ProducePos);Buffer_Critical[ProducePos] = m_serial;printf("Producer %2d finish producing :\n ",m_serial);printf(" position[ %2d ]:%3d \n" ,ProducePos,Buffer_Critical[ProducePos]); //使生产者写的缓冲区可以被多个消费者使用,实现读写同步;ReleaseSemaphore(h_Semaphore[m_serial],n_Thread,NULL);}//消费者进程void Consume(void * p){//局部变量声明;DWORD wait_for_semaphore,m_delay;int m_serial,m_requestNum; //消费者的序列号和请求的数目;int m_thread_request[MAX_THREAD_NUM];//本消费线程的请求队列;//提取本线程的信息到本地;m_serial = ((ThreadInfo*)(p))->serial;m_delay = (DWORD)(((ThreadInfo*)(p))->delay *INTE_PER_SEC);m_requestNum = ((ThreadInfo *)(p))->n_request;for (int i = 0;i<m_requestNum;i++)m_thread_request[i] = ((ThreadInfo*)(p))->thread_request[i];Sleep(m_delay);//循环进行所需产品的消费for(i =0;i<m_requestNum;i++){//请求消费下一个产品printf("Consumer %2d request to consume %2dproduct\n",m_serial,m_thread_request[i]);//如果对应生产者没有生产,则等待;如果生产了,允许的消费者数目-1;实现了读写同步;wait_for_semaphore=WaitForSingleObject(h_Semaphore[m_thread_request[i] ],-1);//查询所需产品放到缓冲区的号int BufferPos=FindBufferPosition(m_thread_request[i]);//开始进行具体缓冲区的消费处理,读和读在该缓冲区上仍然是互斥的;//进入临界区后执行消费动作;并在完成此次请求后,通知另外的消费者本处请求已//经满足;同时如果对应的产品使用完毕,就做相应处理;并给出相应动作的界面提//示;该相应处理指将相应缓冲区清空,并增加代表空缓冲区的信号量;EnterCriticalSection(&PC_Critical[BufferPos]);printf("Consumer%2d begin to consume %2d product\n",m_serial,m_thread_request[i]);((ThreadInfo*)(p))->thread_request[i] =-1;if(!IfInOtherRequest(m_thread_request[i])){Buffer_Critical[BufferPos] = -1;//标记缓冲区为空;printf("Consumer%2d finish consuming %2d:\n",m_serial,m_thread_request[i]);printf(" position[ %2d ]:%3d \n" ,BufferPos,Buffer_Critical[BufferPos]); ReleaseSemaphore(empty_semaphore,1,NULL);}else{printf("Consumer %2d finish consuming product %2d\n",m_serial,m_thread_request[i]);}//离开临界区LeaveCriticalSection(&PC_Critical[BufferPos]);}}8。
浅谈生产者消费者模型(Linux系统下的两种实现方法)

浅谈⽣产者消费者模型(Linux系统下的两种实现⽅法)⽣产者消费者问题是同步问题中的⼀种常见情况,借⽤⼀下维基百科的话⽣产者消费者问题(英语:Producer-consumer problem),也称有限缓冲问题(英语:Bounded-buffer problem),是⼀个多线程同步问题的经典案例。
该问题描述了两个共享固定⼤⼩缓冲区的线程——即所谓的“⽣产者”和“消费者”——在实际运⾏时会发⽣的问题。
⽣产者的主要作⽤是⽣成⼀定量的数据放到缓冲区中,然后重复此过程。
与此同时,消费者也在缓冲区消耗这些数据。
该问题的关键就是要保证⽣产者不会在缓冲区满时加⼊数据,消费者也不会在缓冲区中空时消耗数据。
第⼀种实现信号量配合互斥锁实现,这种⽅法很清晰简单信号量:信号量的特性如下:信号量是⼀个⾮负整数(车位数),所有通过它的线程/进程(车辆)都会将该整数减⼀(通过它当然是为了使⽤资源),当该整数值为零时,所有试图通过它的线程都将处于等待状态。
在信号量上我们定义两种操作: Wait(等待)和 Release(释放)。
当⼀个线程调⽤Wait操作时,它要么得到资源然后将信号量减⼀,要么⼀直等下去(指放⼊阻塞队列),直到信号量⼤于等于⼀时。
Release(释放)实际上是在信号量上执⾏加操作,对应于车辆离开停车场,该操作之所以叫做“释放”是因为释放了由信号量守护的资源。
wait, release在Linux下int sem_wait(sem_t * sem);int sem_post(sem_t * sem);设定两个信号量,empty⽤来表⽰空槽的个数,full⽤来表⽰占有的个数⽣产者在向任务队列⾥放资源时,调⽤sem_wait(&full)来检查队列是否已满,如果满的话,就阻塞,直到有消费者从⾥⾯取资源再苏醒,如果不满,就放资源,并通知消费者来取。
消费者在从任务队列⾥取资源时,调⽤sem_wait(&empty)来检查队列是否为空,如果空的话,就阻塞,直到有⽣产者向⾥⾯放资源再苏醒,如果不空,就取资源,并通知⽣产者来放。
JAVA多线程经典问题--生产者消费者同步队列实现方法

JAVA多线程经典问题--⽣产者消费者同步队列实现⽅法在JAVASE5 中的java.util.concurrent.BlockingQueue⽀持,BlockingQueue是⼀个接⼝但是我们通常可以使⽤LinkedBlockingQueue,它是⼀个⽆界的队列,当然我们还可以使⽤ArrayBlockingQueue,它拥有固定的尺⼨,因此我们可以在他被阻塞之前放⼊有限的元素。
当消费者试图从队列中获取对象时,如果队列为空,那么这些队列还可以挂起消费者任务,多么神奇的功能,那么当队列中有⾜够的元素可以供消费者获取,那么他可以回复消费者任务,⽐使⽤⼀些让⼈难理解的notifyAll wait要简单,并且可靠很多。
简单写了两句class Product {private final int orderNum;public Product(int orderNum) {this.orderNum = orderNum;}public String toString() {return "Product" + orderNum;}}class Producter implements Runnable {private MainQueue main;public Producter(MainQueue main) {this.main = main;}public void run() {Product product = new Product(new Random().nextInt(100));//向队列插⼊⼀个元素,此时consumer任务是获取不了当前这个队列的所即他读取不了⾥⾯的数据main.queue.add(product);System.out.println("the Producter put the " + product+ " in to the queue");}}class Consumer implements Runnable {private MainQueue main;public Consumer(MainQueue main) {this.main = main;}public void run() {while (main.queue.size() > 0) {Product product = null;try {//读队列中的⼀个元素,此时product任务写不进去元素product = main.queue.take();System.out.println("the Consumer get the" + product+ " from the quene");} catch (InterruptedException e) {System.out.println("Consumer interrupted!");}}}}public class MainQueue {//这是⼀个同步队列它只允许⼀个任务插⼊或者删除元素 LinkedBlockingQeque是⼀个⽆界的队列BlockingQueue<Product> queue = new LinkedBlockingDeque<>();Producter producter = new Producter(this);Consumer consumer = new Consumer(this);public MainQueue() {for (int i = 0; i < 10; i++) {new Thread(producter).start();}for (int i = 0; i < 10; i++) {new Thread(consumer).start();}}public static void main(String[] args) {new MainQueue();}}看是不是很简单,运⾏结果如下:the Producter put the Product91 in to the queuethe Producter put the Product50 in to the queuethe Producter put the Product72 in to the queue the Producter put the Product46 in to the queue the Producter put the Product92 in to the queue the Producter put the Product91 in to the queue the Producter put the Product52 in to the queue the Producter put the Product48 in to the queue the Producter put the Product41 in to the queue the Consumer get theProduct91 from the quene the Consumer get theProduct52 from the quene the Producter put the Product72 in to the queue the Consumer get theProduct92 from the quene the Consumer get theProduct50 from the quene the Consumer get theProduct72 from the quene the Consumer get theProduct72 from the quene the Consumer get theProduct91 from the quene the Consumer get theProduct48 from the quene the Consumer get theProduct41 from the quene the Consumer get theProduct46 from the quene 有不⾜之处和错误之处,请留⾔,本⼈虚⼼请教。
生产者消费者同步问题的算法实现

生产者消费者同步问题的算法实现一、实验目的:全面理解生产者与消费者问题模型,掌握解决该问题的算法思想,正确使用同步机制。
二、实验内容:问题描述:一组生产者向一组消费者提供消息,它们共享一个有界缓冲池,生产者向其中投放消息,消费者从中取得消息。
假定这些生产者和消费者互相等效,只要缓冲池未满,生产者可将消息送入缓冲池;只要缓冲池未空,消费者可从缓冲池取走一个消息。
功能要求:根据进程同步机制,编写一个解决上述问题的演示程序,可显示缓冲池状态、放数据、取数据等过程。
放消息取消息n个缓冲区(Buffer)三、编程工具:C、Java、VC或其它可视化语言平台任选四、具体设计要求及有关说明1.有3个生产者进程,分别为P1、P2和P3;2.有4个消费者进程,分别是C1、C2、C3和C4;3.缓冲区单元个数N=15;4.不同的生产进程可生产不同的产品(比如字母、数字、符号);不同的消费进程可有不同的消费方式(比如“显示”、“打印”、“拼接成字符串”、“改变大小写”等)。
自己可任意定义。
实现代码:#include<stdio.h>#include<string.h>void main(){int full=0; //用于判断缓冲池是否为满int emputy=15; //用于判断缓冲池时候为空char buffer[15][10]; //用于存放产品char ch[10]; //用于接收生产的产品和消费的产品int i=0,j=0;int num;int number;int numb;printf("----------------------------------产品使用说明-------------------------------\n");printf("1生产者生产产品\n");printf("2消费者消费产品\n");printf("3生产字母产品\n");printf("4生产数字产品\n");printf("5生产符号产品\n");printf("6消费方式(显示)\n");printf("7消费方式(打印)\n");printf("8消费方式(全部变成小写)\n");printf("9消费方式(全部变成大写)\n");printf("-----------------------------------------------------------------------------\n");while(true){printf("请输入1或者2进行生产或消费:");scanf("%d",&num);if(num==1)//当num为1的时候,生产者生产产品{printf("请输入3或者4或者5生产不同的产品:"); //当number为3时生产字符,为4时生产数字,为5时生产字符scanf("%d",&number);//以下生产字符if(number==3){if(full==15){printf("产品已满!\n");}if(full<15){printf("请输入生产的产品:");scanf(" %s",&ch);for(j=0;j<sizeof(ch);j++){buffer[i][j]=ch[j];}j++;buffer[i][j]='\0';i++;full++;emputy--;printf("产品生产成功!产品为:%s\n",buffer[i-1]);}}//if(number==3)结束//以下生产数字if(number==4){if(full==15){printf("产品已满!\n");}if(full<15){printf("请输入生产的产品:");scanf(" %s",&ch);for(j=0;j<sizeof(ch);j++){buffer[i][j]=ch[j];}j++;buffer[i][j]='\0';i++;full++;emputy--;printf("产品生产成功!产品为:%s\n",buffer[i-1]);}}//if(number==4)结束//以下生产字符if(number==5){if(full==15){printf("产品已满!\n");}if(full<15){printf("请输入生产的产品:");scanf(" %s",&ch);for(j=0;j<sizeof(ch);j++){buffer[i][j]=ch[j];}j++;buffer[i][j]='\0';i++;full++;emputy--;printf("产品生产成功!产品为:%s\n",buffer[i-1]);}}//if(number==5)结束}//if(num==1)结束if(num==2)//当num为2时,消费者消费产品{printf("请输入6或者7或者8或者9选择不同消费方式:"); //当numb为6时显示,为7时打印,为8时转换成小写,为9时转换成大写scanf("%d",&numb);//以下为显示消费模式if(numb==6){if(emputy<15){i--;emputy++;full--;printf("消费成功!显示:%s\n",buffer[i]);}else{printf("产品为空!\n");}}//if(numb==6)结束//以下为打印消费模式if(numb==7){if(emputy<15){i--;emputy++;full--;printf("消费成功!打印:%s\n",buffer[i]);}else{printf("产品为空!\n");}}//if(numb==7)结束//以下为转换成小写消费模式if(numb==8){if(emputy<15){i--;emputy++;full--;printf("消费成功!转换成小写:%s\n",strlwr(buffer[i]));}else{printf("产品为空!\n");}}//if(numb==8)结束//以下为转换成大写消费模式if(numb==9){if(emputy<15){i--;emputy++;full--;printf("消费成功!转换成大写:%s\n",strupr(buffer[i]));}else{printf("产品为空!\n");}}//if(numb==9)结束}//if(num==2)结束}//while结束}//main()结束。
操作系统_生产者与消费者问题的实现

//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));
生产者—消费者问题

第一章、概述1.1 课题背景在多道程序环境下,进程同步问题十分重要,也是一个相当有趣的问题,因而吸引了不少学者对它进行研究,并由此而产生了一系列经典的进程同步问题。
其中比较有代表性的有“生产者—消费者问题” 、“读者—写者问题” 、“哲学家进餐问题”等等。
通过对这些问题的研究和学习,可以帮助我们更好地理解进程同步概念及实现方法。
1.2生产者—消费者问题生产者—消费者问题(Producer_consumer)是一个经典的进程同步问题。
它描述的是:有一群生产者进程在生产产品,并将此产品提供给消费者进程去消费。
为使生产者进程和消费者进程能并发执行,在它们之间设置有个缓冲区的缓冲池,生产者进程可将它所生产的产品放入一个缓冲区中,消费者进程可从一个缓冲区取得一个产品消费。
尽管所有的生产者进程和消费者进程都是以异步的方式运行的,但它们之间必须保持同步,即不允许消费者进程到一个空缓冲区去取产品,也不允许生产者进程向一个已装有消息尚未被取走产品的缓冲区投放产品。
如下图所示:1.3进程同步机制在中引入进程后,虽然提高了资源的利用率和系统的吞吐量,但由于进程的异步性,也会给系统造成混乱,尤其是在它们争用临界资源的时候。
例如,当多个进程去争用一台打印机时,有可能使多个进程的输出结果交织在一起,难于区分;而当多个进程去争用共享变量,表格,链表时,有可能使数据处理出错。
进程同步的主要任务就是使并发执行的诸进程之间能有效地共享资源和相互合作,从而使程序的执行具有可再现性。
1.4进程同步优点进程同步其优点在于能够让操作系统更加有效地对资源进行管理和调度,最大潜力地发挥处理机的性能。
让系统的执行更加畅通无阻,尽可能地让系统少出现一些由于系统资源分配不合理所带来的死锁、死机之类的事情的发生。
保持了处理机的高速运行之后从用户角度来说程序运行所花费的时间就会更短。
从而保证了处理机在相同的时间内有更大的吞吐量。
而把并发进程的同步和互斥问题一般化,就可以得到一个抽象的一般模型,即本次课程设计的任务:生产者—消费者问题。
生产者与消费者算法的实现

实验六信号量厦门大学软件学院吴清强一.实验目的⏹加强对进程概念的理解。
⏹进一步了解并发执行的实质。
⏹分析进程争用资源的现象,学习解决进程互斥的方法⏹了解Linux系统中进程通信的基本原理二.相关知识⏹进程的概念。
⏹进程与程序的区别。
⏹进程并发执行的概念。
⏹进程互斥的概念⏹进程通信的基本原理三.实验内容⏹使用信号量实现有限缓冲区的生产者和消费者问题⏹使用信号量实现读进程具有优先权的读者和写者问题四.实验环境⏹PC + Linux Red Hat操作系统⏹GCC1. 使用信号量实现有限缓冲区的生产者和消费者问题一.程序流程图生产者进程生产流程图消费者进程消费流程图二.源代码1、生产者生产进程函数DWORD WINAPI Producer(LPVOID lpParameter){while(true){for(int j=0;j<4;j++){if(buffer[j]==0){//找到空缓冲区if(lock[j]==false){//同步锁为false,可以进行操作lock[j]=true;//加锁,防止其他线程操作此缓冲区if(buffer[j]<1){//限定一个缓冲区只能存放一个资源++buffer[j];//模拟生产资源cout<<"生产一个资源,放入缓冲区"<<j<<"中"<<endl;lock[j]=false;//解锁break;//一次生产一个}}if(j==3){cout<<"找不到空缓冲区,等待中。
"<<endl;Sleep(2000);}}}}return 0;}2、消费者消费进程函数DWORD WINAPI Customer(LPVOID lpParameter){while(true){for(int n=0;n<4;n++){if(buffer[n]==1){//找到满缓冲区if(lock[n]==false){//同步锁为false,可以进行操作lock[n]=true;//加锁,防止其他线程操作此缓冲区if(buffer[n]>=1){--buffer[n];//模拟消费资源cout<<"消费一个资源,从缓冲区"<<n<<"中取出"<<endl;lock[n]=false;//解锁break;//一次生产一个}}}if(n==3){cout<<"找不到满缓冲区,等待中。
- 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下一些函数认识的更加透彻,例如对进程间的通信,多线程的安全问题,管道问题都有了进一步的了解。