哲学家就餐问题
五个哲学家,共用一张圆桌,分别坐在周围的5张椅子上,在圆桌上有5个碗和五支筷子,平时哲学家进行思考,饥饿时便试图取其左、右靠近他的筷子,只有在他拿到两只筷子时才能进餐。进餐完后,放下筷子思考。
要求:
1、键盘上的按键1-5分别代表5个哲学家申请吃饭
2、要求列出所有可能出现的状况,而且屏幕上必须将最新状态显示出来
(1)哲学家i正在进餐,哲学家i对应的状态显示为“Philos i is eating”,并显示进餐完毕倒计时
(2) 哲学家i申请进餐未果,哲学家i对应的状态显示为“Philos i apply for meal failed”,并且等待5秒。
若5秒内拿到了筷子,状态改为“Philos i is eating”,并显示进餐完毕倒计时;若5秒内没拿到筷子,则显示"Philos i is thinking "
(3) 哲学家i正在思考,哲学家i对应的状态显示为"Philos i is thinking "
3、每位哲学家进餐时间可变,进餐次数不限(程序能一直运行)
基本思路
不申请吃饭时,显示"Philos i is thinking ";按下相应按键(1-5)申请吃饭时,先显示“Philos i apply for meal failed”,再在等待的5s内判断是否拿到了两根筷子,左右两根都拿到了则显示“Philos i is eating”,没拿到两根则不改变显示。
实现
信号量
创建5个信号量来代表5根筷子。创建7个任务,5个任务为哲学家,1个任务用来按键读取,1个任务用来创建前面6个任务,创建完成后挂起自身。
注意:这里要创建5个信号量,需要OS_MAX_EVENTS不小于5,OS_MAX_EVENTS在
OS_CFG.h中更改。OS_TICKS_PER_SEC 为 200,也就是200个timetick为1秒
代码:
#include "includes.h" #define TASK_STK_SIZE 512 //任务堆栈长度 #define TIME 50 //eat_time -> 60-TIME OS_STK StartTaskStk[TASK_STK_SIZE]; //定义任务堆栈区 OS_STK PhilosStk[5][TASK_STK_SIZE]; OS_STK ReadKeyStk[TASK_STK_SIZE]; INT8U PhilosID[]={1,2,3,4,5}; INT16S key; //用于退出uCOS_II的键 INT16S key1[] = {0x1B,0x31,0x32,0x33,0x34,0x35}; //检测输入 INT8U err; INT8U x=0, y=0; //字符显示位置 INT8U i=0; BOOLEAN getKey; OS_EVENT *ChopStick[5]; //声明5个信号量(筷子) void StartTask(void *data); void readKey(void *data); void Philos(void *data); void Eat(INT8U PhID); void Think(INT8U PhID); /************************主函数*********************************************/ void main (void) { char temp[50]; OSInit(); //初始化uCOS_II PC_DOSSaveReturn(); //保存Dos环境 PC_VectSet(uCOS, OSCtxSw); //安装uCOS_II中断 for(i=0;i<5;i++) { ChopStick[i]=OSSemCreate(1); if (ChopStick[i]!=(OS_EVENT *)0) //创建无误则显示 无误 { sprintf(temp,"ChopStick %d create no error",i); PC_DispStr(40,19+i,temp,DISP_BGND_BLACK+DISP_FGND_YELLOW); } } OSTaskCreate(StartTask,0,&StartTaskStk[TASK_STK_SIZE - 1],4); OSStart(); } //*****************************StartTask******************************************** void StartTask(void *pdata) { // char temp[50]; pdata=pdata; OS_ENTER_CRITICAL(); PC_VectSet(0x08, OSTickISR); PC_SetTickRate(OS_TICKS_PER_SEC); OS_EXIT_CRITICAL(); OSStatInit(); OSTaskCreate(readKey, (void*)0, &ReadKeyStk[TASK_STK_SIZE - 1], 5); for(i=0;i<5;i++) { err=OSTaskCreate(Philos,(void *)&PhilosID[i],&PhilosStk[i][TASK_STK_SIZE - 1],i+6); // if(err==OS_NO_ERR) // //test if task is created successfully // { // sprintf(temp,"Philosopher %d create no error %d",PhilosID[i],err); // //OSTimeDly(200); // PC_DispStr(40,0+i,temp,DISP_BGND_BLACK+DISP_FGND_PURPLE); // } } OSTaskSuspend(OS_PRIO_SELF); //创建完成,挂起自己 } //****************************readKey******************************************** void readKey(void *pdata) { char *s = "Philosopher dining problem"; pdata=pdata; for(; ;) { PC_DispStr(x,y,s,DISP_BGND_LIGHT_GRAY+DISP_FGND_RED); PC_DispStr( 0, 24, " <-PRESS 'ESC' TO QUIT-> ", DISP_FGND_BLACK + DISP_BGND_LIGHT_GRAY + DISP_BLINK); if(key==key1[0]) //退出程序 { PC_DOSReturn(); } OSTimeDly(1); } } //*****************************Philos******************************************** //the Philosopher first has to pick up the chopstick on his left hand then right void Philos(void *pdata) { INT32U time; INT8U err1,err2; INT8U PhID; char temp[50]; PhID = *(INT8U *)pdata; for(; ;) { if(key == key1[PhID]) //检测输入 { sprintf(temp,"Philos %d apply for meal failed ",PhID); PC_DispStr(x,PhID,temp,DISP_BGND_BLACK+DISP_FGND_RED); time = OSTimeGet(); //OS_TICKS_PER_SEC = 200 OSSemPend(ChopStick[(PhID-1)],1000,&err1); //请求左右两根筷子 time = OSTimeGet() - time; //第一个信号量等待时间,两个共5s + 1 TimeTick OSSemPend(ChopStick[(PhID%5)],(1001-time),&err2); //防止无限等待 if((err1 == OS_NO_ERR) && (err2 == OS_NO_ERR)) //有两根,吃饭 { Eat(PhID); } else if((err1 == OS_NO_ERR) && (err2 == OS_TIMEOUT)) { OSSemPost(ChopStick[(PhID-1)]); } //一根,释放 else if((err2 == OS_NO_ERR) && (err1 == OS_TIMEOUT)) { OSSemPost(ChopStick[(PhID%5)]); } //一根,释放 else { Think(PhID); } //其他,显示在思考 } else //没申请吃饭,显示在思考 { Think(PhID); OSTimeDly(1); } } } //*****************************Eat()*********************************************** void Eat(INT8U PhID) //显示吃饭并倒计时,吃完释放信号量 { char temp[50]; INT8U j; sprintf(temp,"Philos %d is eating ",PhID); PC_DispStr(x,PhID,temp,DISP_BGND_BLACK+DISP_FGND_GREEN); for(j=TIME; j<60; j++) { if(j<60) { sprintf(temp,"countdown: %d s ",60-j); //倒计时 60 - TIME PC_DispStr(24,PhID,temp,DISP_BGND_BLACK+DISP_FGND_GREEN); OSTimeDlyHMSM(0, 0, 1, 0); } if(j==59) { OS_ENTER_CRITICAL(); OSSemPost(ChopStick[(PhID%5)]); //吃完了,放回去 OSSemPost(ChopStick[(PhID-1)]); OS_EXIT_CRITICAL(); OSTimeDly(1); } } } //*****************************Think()*********************************************** void Think(INT8U PhID) //显示思考 { char temp[50]; sprintf(temp,"Philos %d is thinking ",PhID); PC_DispStr(x,PhID,temp,DISP_BGND_BLACK+DISP_FGND_WHITE); } |
信号量集
实现起来相对于信号量要简单,就不多BB了
#include "includes.h" #define TASK_STK_SIZE 512 //任务堆栈长度 #define TIME 50 //eat_time -> 60-TIME OS_STK StartTaskStk[TASK_STK_SIZE]; //定义任务堆栈区 OS_STK PhilosStk[5][TASK_STK_SIZE]; OS_STK ReadKeyStk[TASK_STK_SIZE]; INT8U PhilosID[] = {1,2,3,4,5}; INT8U Flag_Location[] = {0,3,6,12,24,17}; //每个需要的OS_FLAGS INT16S key; //用于退出uCOS_II的键 INT16S key1[] = {0x1B,0x31,0x32,0x33,0x34,0x35}; //检测输入 INT8U err; INT8U x=0, y=0; //字符显示位置 INT8U i=0; BOOLEAN getKey; char *s1 = "Chop create no error"; OS_FLAG_GRP *Chop; //声明信号量集 void StartTask(void *data); void readKey(void *data); void Philos(void *data); void Eat(INT8U PhID); void Think(INT8U PhID); /************************主函数*********************************************/ void main (void) { OSInit(); //初始化uCOS_II PC_DOSSaveReturn(); //保存Dos环境 PC_VectSet(uCOS, OSCtxSw); //安装uCOS_II中断 Chop = OSFlagCreate(255, &err); //初始化信号量集 if(err == OS_NO_ERR) { PC_DispStr(40,19,s1,DISP_BGND_BLACK+DISP_FGND_YELLOW); } OSTaskCreate(StartTask,0,&StartTaskStk[TASK_STK_SIZE - 1],4); //创建StartTask OSStart(); //启动多任务管理 } //*****************************StartTask******************************************** void StartTask(void *pdata) { // char temp[50]; pdata=pdata; OS_ENTER_CRITICAL(); PC_VectSet(0x08, OSTickISR); //安装时钟中断向量 PC_SetTickRate(OS_TICKS_PER_SEC); //设置时钟频率 OS_EXIT_CRITICAL(); OSStatInit(); for(i=0;i<5;i++) //创建5个任务 { err=OSTaskCreate(Philos,(void *)&PhilosID[i],&PhilosStk[i][TASK_STK_SIZE - 1],10-i); // if(err==OS_NO_ERR) // //test if task is created successfully // { // sprintf(temp,"Philosopher %d create no error %d",PhilosID[i],err); // //OSTimeDly(200); // PC_DispStr(40,0+i,temp,DISP_BGND_BLACK+DISP_FGND_PURPLE); // } } OSTaskCreate(readKey, (void*)0, &ReadKeyStk[TASK_STK_SIZE - 1], 5); OSTaskSuspend(OS_PRIO_SELF); } //****************************readKey******************************************** void readKey(void *pdata) { char *s = "Philosopher dining problem"; pdata=pdata; for(; ;) { PC_DispStr(x,y,s,DISP_BGND_LIGHT_GRAY+DISP_FGND_RED); PC_DispStr( 0, 24, " <-PRESS 'ESC' TO QUIT-> ", DISP_FGND_BLACK + DISP_BGND_LIGHT_GRAY + DISP_BLINK); getKey = PC_GetKey(&key); if(key==key1[0]) //退出程序 { PC_DOSReturn(); } OSTimeDly(1); } } //*****************************Philos******************************************** //the Philosopher first has to pick up the chopstick void Philos(void *pdata) { INT8U j; INT8U PhID; char temp[50]; PhID = *(INT8U *)pdata; //提取输入参数1 ~ 5 for(; ;) { if(key == key1[PhID]) //检测输入的是不是对应的,是则请求信号量集 { sprintf(temp,"Philos %d apply for meal failed ",PhID); //OS_TICKS_PER_SEC = 200 PC_DispStr(x,PhID,temp,DISP_BGND_BLACK+DISP_FGND_RED); //请求time_out = 5s OSFlagPend(Chop, (OS_FLAGS)Flag_Location[PhID], OS_FLAG_WAIT_SET_ALL+OS_FLAG_CONSUME, 1000, &err); if(err == OS_NO_ERR) //请求成功则显示吃饭 { Eat(PhID); } else { Think(PhID); } } else //非对应则显示正在思考 { Think(PhID); OSTimeDly(1); } } } //*****************************Eat()*********************************************** void Eat(INT8U PhID) //显示吃饭并倒计时,吃完释放信号量集 { char temp[50]; INT8U j; sprintf(temp,"Philos %d is eating ",PhID); PC_DispStr(x,PhID,temp,DISP_BGND_BLACK+DISP_FGND_GREEN); for(j=TIME; j<60; j++) { if(j<60) //倒计时60 - TIME { sprintf(temp,"countdown: %d s ",(60-j)); PC_DispStr(24,PhID,temp,DISP_BGND_BLACK+DISP_FGND_GREEN); OSTimeDlyHMSM(0, 0, 1, 0); } if(j==59) //倒计时结束 -> 信号量集相应位置1 { OSFlagPost(Chop, (OS_FLAGS)Flag_Location[PhID], OS_FLAG_SET, &err); } } } //*****************************Think()*********************************************** void Think(INT8U PhID) //显示思考 { char temp[50]; sprintf(temp,"Philos %d is thinking ",PhID); PC_DispStr(x,PhID,temp,DISP_BGND_BLACK+DISP_FGND_WHITE); } |