操作系统算法之生产者消费者算法模拟

操作系统 同时被 2 个专栏收录
4 篇文章 0 订阅
3 篇文章 0 订阅

操作系统算法之生产者消费者算法模拟

实验目的

本实验通过参考和调试生产者-消费者模拟程序,进一步认识进程并发执行的实质,加深对进程竞争关系,协作关系的理解,掌握使用信号量机制与P、V操作来实现进程的同步与互斥。进而完成本实验的要求。

实验要求

参考生产者-消费者模拟程序,完成下题要求:
桌子上有一空盘,允许存放一只水果。爸爸可向盘中放苹果,也可向盘中放桔子,儿子专等吃盘中的桔子,女儿专等吃盘中的苹果。规定当盘空时一次只能放一只水果供吃者取用。
(1)使用P、V操作实现爸爸、儿子、女儿三个并发进程的同步的算法。
(2)用高级语言编写一个程序,爸爸、儿子、女儿进程并发执行过程,并采用信号量机制与P、V操作实现进程间同步。
(3)程序不能无休止运行下去,可自行设定停止条件,如爸爸往盘中放入四个水果后结束。
每个进程由一个进程控制块(PCB)表示,进程控制块结构可参考如下:

typedef struct Process//进程PCB
 {		
	char name[10];			  //进程名
	int roleFlag;			  //进程类型(1:生产者  0: 消费者)
	int currentState;		  //进程状态(1: 可运行态  0: 阻塞态)
	int currentStep;		  //断点
	char fruit[20]			  //水果名称
}Process;

实验分析

从题目要求来看,显而易见,生产者为父亲,消费者为儿子和女儿。桌子上有一空盘,允许存放一只水果。爸爸可向盘中放苹果,也可向盘中放桔子,儿子专等吃盘中的桔子,女儿专等吃盘中的苹果。规定当盘空时一次只能放一只水果供吃者取用。首先,根据题意可知,此题中包含三个进程,分别是父亲进程、儿子进程和女儿进程。其次,要根据每个关注的对象,来确定信号量。父亲进程关注的对象是盘子是否为空;儿子进程关注的对象是盘子里是否有橘子;女儿则是关注盘子里是否有苹果。分析知,只有当爸爸在盘子里放入了水果之后,儿子女儿才能取水果吃。故存在儿子和女儿进程等待爸爸进程的现象,即存在进程相互等待的现象,所以涉及进程同步问题。

PV原语表示

void Father()//父亲进程原语
 {
 	while(true)
 	{
 		P(empty);
 		P(mutex);
 		put_in();//放入橘子或者苹果
		V(mutex);
		if(放入苹果)
		{
			V(apple); 
		} 
		else
		{
			V(orange);
		}
	  } 
 }
//儿子女儿进程原语表示
void Son()
 {
 	while(true)
 	{
 		P(orange);
 		P(mutex);
 		orange_out();//儿子拿走橘子
		V(mutex);
		V(empty); 
	 }
 }
 void Daughter()
 {
 	while(true)
 	{
 		P(apple);
 		P(mutex);
 		apple_out();//女儿拿走苹果 
		V(mutex);
		V(empty); 
	 }
 }

整体流程图

在这里插入图片描述

父亲进程流程图

在这里插入图片描述

儿子女儿进程流程图

在这里插入图片描述

算法代码

#include "stdio.h"
#include "time.h"
#include "stdlib.h"
#include "string.h"
#include "windows.h"
#define processNum 3		  //进程个数 
typedef struct Process        //进程PCB
 {		
	char name[10];			  //进程名
	int roleFlag;			  //进程类型(1:生产者(父亲); 0:消费者(儿子女儿)
	int currentState;		  //进程状态(1: 可运行态  0: 阻塞态)
	int currentStep;		  //断点
	char fruit[20];           //水果名称
	int code;			      //进程编号
	
}Process;

typedef struct semaphore      //信号量 
{
	int value; 				  //信号量的值
	int *pcq;			      //信号量队列指针
}semaphore; 

int producerCongestionQueue[processNum];	//等待信号量empty的阻塞队列
int consumerOrangeCongestionQueue[processNum];	//等待信号量orange的阻塞队列
int consumerAppleCongestionQueue[processNum];	//等待信号量orange的阻塞队列
int shareCongestionQueue[processNum];		//等待信号量mutex的阻塞队列

semaphore empty={1,producerCongestionQueue };
semaphore orange={0,consumerOrangeCongestionQueue };
semaphore apple={0,consumerAppleCongestionQueue };
semaphore mutex={1,shareCongestionQueue};

Process process[processNum];  //进程集合

int fruitcount=0;             //水果计数器 
int fatherputsnum=0;          //父亲一共有多少个水果 
//函数声明 
void initProcess();
void wakeup(int *pcq);
void block(int pcq[],int code);
void P(semaphore *s,Process *p);
void V(semaphore *s,Process *p);
void Father(Process *p);
void SonOrDaughter(Process *p);
void rr();

void initProcess()//初始化函数 
{
	int i;
	for(i=0;i<processNum;i++)
	{
		if(i==0)
		{
			strcpy(process[i].name, "Father");
			process[i].roleFlag=1;
		}
		if(i==1)
		{
			strcpy(process[i].name, "Son");
			process[i].roleFlag=0;
		}
		if(i==2)
		{
			strcpy(process[i].name, "Daughter");
			process[i].roleFlag=0;
		}
		process[i].currentState = 1;//就绪状态 
		process[i].currentStep = 1;//第一步 
		process[i].code = i + 1;
		producerCongestionQueue[i] = 0;
		consumerOrangeCongestionQueue[i] = 0;
		consumerAppleCongestionQueue[i] = 0;
		shareCongestionQueue[i] = 0;
	}
}
void wakeup(int *pcq)  //唤醒进程操作 
{
	int code = pcq[0] - 1;				//取出队首进程
	process[code].currentState = 1;		//进程置为就绪态
	                                    //当进程被唤醒后继续执行任务
	if(process[code].roleFlag == 1)    //生产者,父亲
	{
		if (process[code].currentStep == 2)
		{
			printf("%s wakes up, can put fruit into plate!\n", process[code].name);
		}
		if(process[code].currentStep == 3)
		{
			printf("%s apply for putting fruit into plate sucessfully!\n", process[code].name);
		} 
	}
	if(process[code].roleFlag == 0)    //消费者,儿子女儿 
	{
		if(process[code].currentStep == 1)
		{
			printf("%s wakes up,there are fruit in plate!\n", process[code].name);
		}
		if (process[code].currentStep == 2)
		{
			printf("%s apply for getting fruit from plate sucessfully!\n", process[code].name);
		}

	}
	process[code].currentStep++;
	int i;
	for (i=1; (i < processNum) && (pcq[i] != 0); i++) 	//删除队首进程
	{	
		pcq[i - 1] = pcq[i];
		if (pcq[i - 1] > processNum) 
		{
			pcq[i - 1] = 0;
		}
	}
	pcq[i - 1] = 0;
} 
void block(int pcq[],int code)                   //阻塞进程 操作 
{
	int i;
	process[code - 1].currentState = 0;			//进程置为阻塞态
	for (i = 0; i < processNum; i++) 
	{
		if (!pcq[i]) 
		{
			pcq[i] = code;
			break;
		}
	}
}
void P(semaphore *s,Process *p)                 //P操作 
{
	s->value -= 1;
	if (s->value < 0)
	{
		p->currentState = 0;
		p->currentStep++;
	}
	if (s->value >= 0)                           //资源条件满足 
	{
		if (p->roleFlag == 1) {					//父亲准备放水果 
			if (p->currentStep == 2) 
			{
				printf("%s:Plate is empty now.\n", p->name);
			}
			else if (p->currentStep == 3) 
			{
				printf("%s:Can put fruits into plate.\n", p->name);
			}
		}
		else if (p->roleFlag == 0)             //儿子女儿准备取水果 
		{			
			if (p->currentStep == 1) 
			{
				printf("%s:There are fruits in plate.\n", p->name);
			}
			else if (p->currentStep == 2) 
			{
				printf("%2:Can get fruit from plate!\n", p->name);
			}
		}
		p->currentStep++;		
	}
	else if (s->value < 0)						//资源条件不满足满足
	{
		if (p->roleFlag == 1) {					//父亲不能放水果,即生产者进程被阻塞 
			if (p->currentStep == 2) {
				printf("%s:Plate is not empty,Block!\n", p->name);
			}
			else if (p->currentStep == 3) {
				printf("%s:Can`t put fruit into plate!\n", p->name);
			}
		}
		else if (p->roleFlag == 0) {			//盘子中没有水果,即消费者进程被阻塞 
			if (p->currentStep == 1) {
				printf("%s:No fruits in plate, Block!\n", p->name);
			}
			else if (p->currentStep == 2) {
				printf("%s:Can't get fruit from plate!\n", p->name);
			}
		}
		block(s->pcq, p->code);		          //阻塞进程		
	}
}
void V(semaphore *s,Process *p)               //V操作 
{
	s->value += 1;
	if (p->roleFlag == 1) 					//父亲释放对盘子的访问权 
	{				
		if (p->currentStep == 5)
		 {
			printf("%s:Release plate access right.\n", p->name);
		}
		else if (p->currentStep == 6) {
			printf("%s:Fruits are put into plate!\n", p->name);
		}
	}
	else if (p->roleFlag == 0) 				//消费者释放对盘子的访问权 
	{			
		if (p->currentStep == 4) 
		{
			printf("%s:Release plate access right.\n", p->name);
		}
		else if (p->currentStep == 5) {
			printf("%s:Fruits are got out from plate.\n", p->name);
		}
	}

	if (s->value <= 0) 
	{

		wakeup(s->pcq);
	}
	p->currentStep++;
 } 
void Father(Process *p)          //生产者进程,即父亲的进程 
{
	int orangeOrApple;
	switch (p->currentStep) 
	{		
	case 1:							//1 放水果							
		orangeOrApple = rand() % 2; //随机数0和1,0表示苹果,1表示橘子 
		if (orangeOrApple == 0)
			strcpy(p->fruit, "Apple");
		else
			strcpy(p->fruit, "Orange");
		p->currentStep++;
		break;
	case 2:							//2 申请空盘子 
		P(&empty, p);
		break;
	case 3:							//3 申请的盘子访问权 
		P(&mutex, p);
		break;
	case 4:					       //4 将水果放进盘子里	
		printf("%s puts an %s into plate. This is No.%d fruit.\n", p->name, p->fruit,fruitcount+1);
		p->currentStep++;
		break;
	case 5:						   //5 释放对盘子的访问权
		V(&mutex, p);
		break;
	case 6:
		fruitcount++;
		if (!p->fruit)			  //6 水果已经放入盘子 ,通知两个消费者		   
			V(&apple, p);
		else
			V(&orange, p);
		p->currentStep = 1;
		break;
	}
}
void SonOrDaughter(Process *p)
{
	if (!strcmp(p->name, "Son"))  //消费者是儿子
	{
		switch (p->currentStep) 
		{
		case 1:					 //1 申请从盘子中拿水果
			P(&orange, p);
			break;
		case 2:					//2 申请盘子的访问权 
			P(&mutex, p);
			break;
		case 3:					//3 从盘子里取水果
			printf("%s gets an orange from plate.\n", p->name);
			p->currentStep++;
			break;
		case 4:					//4 释放对盘子的访问权
			V(&mutex, p);
			break;
		case 5:					//5 已经从盘子里取走水果,通知父亲放水果 
			V(&empty, p);
			p->currentStep = 1;
			break;
		}
	}
	if (!strcmp(p->name, "Daughter"))//消费者是女儿,过程类似与儿子 
	{
		switch (p->currentStep) 
		{
		case 1:					//1 申请从盘子中拿水果
			P(&apple, p);
			break;
		case 2:				    //2 申请盘子的访问权 
			P(&mutex, p);
			break;
		case 3:					//3 从盘子里取水果
			printf("%s gets an apple from plate.\n", p->name);
			p->currentStep++;
			break;
		case 4:				    //4 释放对盘子的访问权		
			V(&mutex, p);
			break;
		case 5:				    //5 已经从盘子里取走水果,通知父亲放水果 
			V(&empty, p);
			p->currentStep = 1;
			break;
		}
	}
}
void rr()
{
	printf(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");
	printf("Please input the amount of fruits father has is: ");
	scanf("%d",&fatherputsnum);
	printf("The amount of fruits father has is: %d\n",fatherputsnum);
	printf(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n\n\n");
	
	printf(">>>>>>>>>>>>>>>>>>>>Process begins......>>>>>>>>>>>>>>>>>>>>>>>\n");

	Process *p;
	while(fruitcount<fatherputsnum)
	{
		p = &process[rand() % processNum];		//随机选取进程集合内某一进程
		if (!p->currentState) 
		{					//选取的进程若为阻塞态,重新选取其它可执行进程
			continue;
		}
		if (p->roleFlag) //父亲 
		{		
			Father(p);
		}
		else            //儿子女儿 
		{
			SonOrDaughter(p);
		}
	}

}
int main()
{
	printf("\n*******************************************************");
	printf("\n*           实验二:生产者—消费者实验模拟            *");
	printf("\n*******************************************************\n\n"); 
	printf("\n");
	initProcess();
	rr();
	printf(">>>>>>>>>>>>>>>>>>>>Process ends......>>>>>>>>>>>>>>>>>>>>>>\n");
	printf("\n\n");
	return 0;
 } 

实验结果截图(部分)

当水果数为5个时:
在这里插入图片描述在这里插入图片描述

实验分析

1.深刻理解了进程之间的同步,进程的同步是指多个进程中发生的事件存在着某种时序关系,必须协同动作、相互配合,从而来共同完成一个任务。也就是说,一个进程运行到某一点时要求另一个伙伴进程为它提供消息,在未获得消息之前,该进程处于等待状态,获得消息后被唤醒进入就绪状态;
2.同时也要区分与进程的互斥的区别,进程的互斥是因为双方竞争同一种资源从而使两者产生了相互制约的互斥关系,是一种间接作用;
3.对问题认真分析,确定问题属于哪种类型,例如本次实验就是典型的生产者-消费者问题,确定临界资源(系统中某些资源一次只允许一个进程使用这样的资源),先用PV原语写出各自逻辑进程,再进一步完善过程;
4.生产者与消费者关心的东西不同,生产者关心的是空的部分,消费者应该关心的是满的部分,因此,有资源竞争的是生产竞争的是生产者和生产者之间,消费者和消费者之间,所以,应该理解为两个不同的临界区,生产者关心生产者的临界区,消费者关心的是消费者的临界区,这样我们可以定义两个互斥信号量,分别管理各自的临界区。
5.进程同步互斥的实现有许多方法,有硬件实现和软件实现,但都不能同时满足空闲让进、忙则等待、有限等待、让权等待,而信号量的提出解决了这些问题,并且可以实现;
6.应当注意,当两个P操作在一起时,他们的顺序至关重要,一个同步P操作与一个互斥P操作的顺序为同步P、操作应该在互斥P操作前面,否则会发生死锁。

以上就是实验所有内容,代码仅供参考!

  • 0
    点赞
  • 2
    评论
  • 11
    收藏
  • 打赏
    打赏
  • 扫一扫,分享海报

生产者消费者问题算法实现》 设计思想 因为有多个缓冲区,所以生产者线程没有必要在生成新的数据之前等待最后一个数据被消费者线程处理完毕。同样,消费者线程并不一定每次只能处理一个数据。在多缓冲区机制下,线程之间不必互相等待形成死锁,因而提高了效率。   多个缓冲区就好像使用一条传送带替代托架,传送带上一次可以放多个产品。生产者在缓冲区尾加入数据,而消费者则在缓冲区头读取数据。当缓冲区满的时候,缓冲区就上锁并等待消费者线程读取数据;每一个生产消费动作使得传送带向前移动一个单位,因而,消费者线程读取数据的顺序和数据产生顺序是相同的。 可以引入一个count计数器来表示已经被使用的缓冲区数量。用hNotEmptyEvent 和hNotFullEvent 来同步生产者消费者线程。每当生产者线程发现缓冲区满( count=BufferSize ),它就等待hNotEmptyEvent 事件。同样,当消费者线程发现缓冲区空,它就开始等待hNotEmptyEvent。生产者线程写入一个新的数据之后,就立刻发出hNotEmptyEvent 来唤醒正在等待的消费者线程;消费者线程在读取一个数据之后,就发出hNotFullEvent 来唤醒正在等待的生产者线程。 程序的设计思想大致为:设置一while循环,pi生产者访问临界区,得到权限访问缓冲区,如果缓冲区满的,则等待,直到缓冲区非满;访问互斥锁,当得到互斥锁且缓冲区非满时,跳出while循环,开始产生新数据,并把数据存放于Buffer缓冲区中,当数据存放结束则结束临界区;接着唤醒消费者线程;ci消费者访问临界区,得到权限访问缓冲区,如果缓冲区为空,没有可以处理的数据,则释放互斥锁且等待,直到缓冲区非空;当等到缓冲区非空时,跳出while循环;消费者获得数据,并根据所获得的数据按类别消费(当消费者获得的数据为大写字母时,则把大写字母转换成小写字母,并显示;当消费者获得的数据为小写字母时,则把小写字母转换成大写字母,并显示;当消费者获得的数据为字符0、1、2、……8、9时,把这些字符直接显示到屏幕;当消费者获得的数据为符号(+、-、*、\……)时,把这些符号打印成7行7列的菱形);处理完数据后,结束临界区;接着唤醒生产者线程。
©️2022 CSDN 皮肤主题:数字20 设计师:CSDN官方博客 返回首页

打赏作者

Endless Road

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值