利用多线程实现哲学家就餐.docx
《利用多线程实现哲学家就餐.docx》由会员分享,可在线阅读,更多相关《利用多线程实现哲学家就餐.docx(15页珍藏版)》请在冰豆网上搜索。
利用多线程实现哲学家就餐
操作系统课程设计报告
题目:
利用多线程机制实现“哲学家就餐”
所在学院:
信息工程学院
班级:
计科1102
学号:
*********
***********************
**********
日期:
2014年1月7日
1.设计目的
1.理解“哲学家就餐”模型,掌握基本的同步、互斥算法。
2.理解操作系统中进程和线程的异同,理解并发过程中的死锁现象。
3.掌握Linux多线程创建及并发机制、线程同步机制。
4.掌握和使用LinuxPOSIX线程接口实现无死锁的哲学家就餐问题。
2.需求分析
2.1问题描述
有五个哲学家围坐在一圆桌旁,桌中央有一盘通心粉,每人面前有一只空盘子,每两人之间放一只筷子,即共5只筷子。
每个哲学家的行为是思考和进餐。
为了进餐,每个哲学家必须拿到两只筷子,并且每个人只能直接从自己的左边或右边去取筷子。
思考时则同时将两支筷子放回原处
(此图中以叉子代表筷子)
规则:
只有拿到两只筷子时,哲学家才能吃饭;
如果筷子已经在他人手上,则该哲学家必须等到他人吃完之后才能拿到筷子;
任何一个哲学家在自己没有拿到两只筷子吃饭之前,决不放下自己手中的筷子。
由此出现的问题:
可能出现死锁问题,因为当五个哲学家都饥饿时,都拿着一支筷子,这样就可能五个哲学家都用不上餐
2.2问题分析
该问题可用记录型信号量或者是AND型信号量解决。
记录型信号量解决:
经分析可知,放在桌子上的筷子是临界资源,在一段时间内只允许一位哲学家使用,为了实现对筷子的互斥使用,可以用一个信号量表示一只筷子,由这五个信号量组成信号量数组。
当哲学家饥饿时总是先拿其左边的筷子,成功后,再去拿右边的筷子,又成功后方可就餐。
进餐完,又先放下他左边的筷子,再放下右边筷子。
这个算法可以保证不会有两个相邻的哲学家同时就餐,但有可能引起死锁。
AND型信号量解决:
在哲学家就餐过程中,要求每个哲学家先获得两个临界资源后方能就餐,这在本质上就是AND同步问题,故用AND信号量机制可获得最简洁的解法。
2.3解决方法
对于死锁问题可采取这样的几种解决方法:
(1)至多只允许四个哲学家同时进餐,以保证至少有一个哲学家可以进餐,最终总会释放出他所用过的两只筷子,从而可使更多的哲学家进餐;
(2)仅当左右两只筷子均可用时,才允许哲学家拿起筷子就餐
(3)规定奇数号哲学家先拿起右边筷子,然后再去拿左边筷子,而偶数号哲学家则相反。
(4)把筷子顺序编号fk0,fk1,fk2,fk3,fk4,给每个哲学家分配
筷子时,必须依从小号到大号(或者相反顺序)进行。
本实验,我们采用第二种解决方法
3.程序流程图
3.1主程序流程图
程序由开始到定义筷子信号量tool[5],到定义哲学家类对象P1-P5,哲学家的状态改变用p.chang()来控制,输出当前状态图后判断程序结束开始返回上一菜单。
如图2-1所示。
图3-1主程序模块流程图
3.2状态改变模块chang()流程图:
其中重要的是状态改变模块chang(),开始后先判断哲学家的状态,若哲学家的状态为1,即处于吃饭状态则,执行放下筷子(资源)操作;若处于状态2,则改变哲学家状态为等待,并将哲学家状态设置为0;若哲学家处于状态0,则判断左右手的筷子是否都为空闲,若都为空闲则拿起左右筷子,并将哲学家状态改为1.如图2-2所示。
图3-2状态改变模块Chang()流程图
3.3哲学家状态模块print()流程图:
哲学家状态输出图先定义一个状态数i,判断i的值,根据i的值来输出哲学家的状态。
如图3-3所示。
图3-3返回哲学家状态模块print()流程图
3.4返回餐具状态模块
定义一个字符串型的变量state,当a的值为true时,state为“闲”,当a的值为false时,state为“用”。
如图2-4所示。
图3-4返回餐具状态模块图
4.内容与详细设计
4.1开发内容
现代操作系统引入并发程序设计技术之后,程序的执行不再是顺序的,一个程序为执行完而另一个程序就已经开始执行,程序外部的顺序特性消失,程序与计算不再一一对应。
于是人们引入进程来描述这种变化。
一组进程在执行时间上是重叠的,进程即并发执行。
在多个进程并发运行的过程中,进程之间可能是无关的,也可能是交互的。
交互进程之间可能产生的关系,包括竞争和协作。
并发进程中与共享变量有关的程序段成为临界区,共享变量所代表的资源成为临界资源。
有多种方法可以实现对临界区、临界资源的管理。
其中最基本的方法是设置相应的信号量和P、V操作。
在并发进程执行的过程中,对共享资源的竞争可能产生死锁问题,既一个进程集合中的每个进程都在等待只能由此集合中的其他进程才能引发的事件,而无限期陷入僵持的局面。
解决死锁问题的方法包括:
通过指定资源申请占有及释放的策略来彻底防止死锁的发生;根据特殊算法决定资源分配和去配策略,避免死锁的发生;对死锁情况进行检测并解除。
综上,本模拟程序应当模拟进程的并发执行,对临界资源的互斥访问和死锁情况的发生和防止。
模拟程序的编写过程所使用的技术路线为:
由于算法简单,不定义独立的P、V操作,而是将之融入对资源的申请及释放操作之中。
对于进程的模拟对象即“哲学家”只定义必要的状态包括“思考”(不申请资源的状态)、“等待”(申请资源但无法获得全部资源的等待状态)和“吃面”(获得所申请的所有资源的运行状态)以及占有资源的数量。
而对于资源即“筷子”,除了确定资源是否被利用的状态之外定义了资源是否有其他的申请者这一状态值。
所有状态值都是用枚举表示。
防止死锁产生的策略采用方法为:
如果不能申请并占有到两支“筷子”(所有的资源),那么就不占有任何一支。
这个方法实际上是破坏了产生死锁的第二个条件即“占有和等待条件”。
主要功能
在这个模拟程序中,使用者需要的功能非常简单:
根据使用者控制“哲学家拿筷子”和“哲学家放下筷子”这两个动作,实现五位“哲学家”和五支“筷子”状态的变化。
同时,使用者可以选择改变资源分配策略,防止死锁或不防止死锁。
最后,使用者还可以退出程序或重置“哲学家”及“筷子”的状态。
4.2数据结构
程序中定义一个哲学家类,包含两个私有对象和四个共有对象。
(1)Number对象:
哲学家的编号。
(2)Status对象:
用于保存当前该哲学家的状态,0表示正在等待(即处于饥饿状态)1表示得到餐具正在吃饭,2表示正在思考。
(3)Philosopher(intnum)方法:
哲学家类构造函数,参数num表示哲学家编号
(4)Find()const方法:
返回该哲学家编号。
(5)Getinfo()const方法:
返回哲学家当前状态。
(6)Chang()方法:
根据题目要求改变哲学家的状态(等待->进餐->思考->等待.......)。
另外,程序中包含一个公有对象,bool类型数组tool[5],用来保存5把餐具
状态:
ture表示该餐具当前空闲,false表示该餐具当前正被使用。
程序中还包含两个公有函数:
print和toolstatus。
Print用来返回一个哲学家的状态,toolstatus用来返回一个餐具的状态。
4.3主函数
#include
#include
#include
#include
#include
intmain()
{
charchoice[10];
while
(1)
{
cout<<"----------模式选择:
----------"<cout<<"----------1.死锁:
----------"<cout<<"----------2.解决方案:
---------"<cout<<"----------3.结束:
----------"<cout<<"请选择模式"<cin>>choice;
switch(choice[0])
{
case'1':
Sisuo();break;
case'2':
Psisuo();break;
case'3':
cout<<"退出程序"<exit(0);
break;
default:
cout<<"请正确选择功能号(1-3)!
"<break;
}
}
}
usingnamespacestd;
booltools[5];//存放筷子的状态true表示闲
CRITICAL_SECTIONcs;//临界区结构对象
classPhilosopher//哲学家类
{
private:
intnumber;//哲学家编号
intstatus;//哲学家状态
public:
Philosopher(intnum=0):
status
(2),number(num){}
intfind()const{returnnumber;}//返回哲学家编号
intgetinfo()const{returnstatus;}//返回哲学家状态
voidChange();
voiddeadlock();
4.4改变哲学家状态
voidPhilosopher:
:
Change()//改变哲学家状态
{
EnterCriticalSection(&cs);
if(status==1)
{
tools[number%5]=true;
tools[(number+5-1)%5]=true;
status=2;
}
elseif(status==2)
{
status=0;
}
elseif(status==0)
{
if(tools[number%5]&&tools[(number+5-1)%5])
{
tools[number%5]=false;
tools[(number+5-1)%5]=false;
status=1;
}
}
LeaveCriticalSection(&cs);
}
4.5死锁状态改变
voidPhilosopher:
:
deadlock()//死锁状态改变
{
EnterCriticalSection(&cs);
if(status==0)
{
if(tools[number%5])
{
tools[number%5]=false;
}
if(tools[(number+5-1)%5])
{
tools[(number+5-1)%5]=false;
}
if(tools[(number)%5]==false&&tools[(number+5-1)%5]==false)
status==1;
}
elseif(status==1)
{
tools[number%5]=true;
tools[(number+5-1)%5]=true;
status=2;
}
elseif(status==2)
{
status=0;
}
LeaveCriticalSection(&cs);
}
4.6返回哲学家状态和筷子状态
stringprint(Philosopher*pA)//返回哲学家状态
{
inti=pA->getinfo();
stringstr;
if(i==0)
str="等待";
elseif(i==1)
str="吃面";
elsestr="思考";
returnstr;
}
stringtoolstatus(boola)//返回筷子状态
{
stringstate;
if(a==true)
state="闲";
if(a==false)
state="用";
returnstate;
5.测试用例及运行结果以及分析
程序开始,进入程序按照模式选择,选择1.出现状态说明图如图5-1,和哲学家们发生死锁图如图5-2,图5-3.
图5-1状态说明图
图5-2选择1后显示的状态
图5-3死锁状态,所有哲学家吃不到面
选择2解除死锁后,哲学家能够吃上面。
如图5-4所示。
图5-4选择2解除死锁后的哲学家吃上面
6.心得体会
经过了一周的时间,完成了这次课程设计。
通过这次课程设计,我学到了许多课本上学不到的知识,正所谓实践出真知,在一开始毫无头绪的情况下,借鉴网上的一些资料,不断的琢磨,思考,终于明白问题的一些基本原理。
于是,开始自己摸索,在同学的帮助下,顺利的完成了此次课程设计。
透过这次课程设计,我也认识到一些生活的道理,互相帮助远远比一个人干来的迅速而且效率。
同时在遇到困难时,要反复思考,研究,终会有调试出程序的灵感。
设计中总会遇到这样那样的问题,遇到问题势必要自己分析问题,解决问题,如利用互联网。
这次课程设计也加强了我上网查数据检索问题的能力。