哲学家就餐问题

哲学家就餐问题之解

摘  要:

本文讨论了如何使用信号量解决操作系统中经典的哲学家就餐问题,探讨了并发进程进行同类资源竞争所引发的进程死锁,饥饿的相关解决方案;在多道程序的系统环境中,由于资源的数量远远不能满足并发进程的需求,并发进程执行过程中要进行同类资源竞争,但是随之带来的就是进程死锁和进程饥饿。为此,引入PV操作和信号量,对并发进程执行进行条件控制,使得并发进程在在竞争同类资源时不会出现以上两种状况,以达到并发进程正常执行的目的。

关键词:哲学家问题,操作系统,P操作、V操作,进程同步与互斥,资源分配


1. 引言:

哲学家就餐问题由Dijkstra提出的多进程之间同步访问有限资源的著名案例,哲学家进餐问题(如图1-1所示)讲述了五名哲学家围坐在一张圆桌旁,桌子中央有一盘通心面,每个人面前有一个空盘子,每两个人之间放一只筷子(每位哲学家进餐是一个进程,筷子是竞争的临界资源)。在进餐时,每个人只能从自己的左边和右边取筷子,要同时拿到两根筷子哲学家方能就餐;就餐完毕后,哲学家将手中的筷子放回原位,并进入思考状态;如果不能同时拿到两根筷子,哲学家进入饥饿状态;如果每位哲学家仅拿到一只筷子,都等待相邻哲学家放下筷子,每位哲学家将会都将会无休止的等待下去,陷入死锁。为了使每位哲学家都能顺利就餐,以下提供了几种不同解决方案!

          图1-1

2. 求解方法

2.1方案一:

设置互斥信号量,每次至多4个哲学家进餐

五位哲学家同时进餐的情况下,如果每位哲学家都先拿起自己左边或右边的筷子,那么每个哲学家就都拿到了一只筷子,而无休止的等待旁边的哲学家放下筷子,结果他们都不能进餐,出现进程死锁!为防止出现的危险,考虑每次至多允许4位哲学家同时进入餐厅,第5位只有在已进入就餐的哲学家退席后才去就餐。此时最多4位哲学家哲学家就坐,因此至少保证有一位哲学家能够拿到左右两只筷子,从而完成就餐。

为此,5只筷子互斥使用,筷子编号0-4,对应设置五个初值为1的互斥信号量,此外,餐厅仅提供4个位子,需设置初值为4的同步信号量seat管理4个位置,即:

P(Chopstick[i])//拿起筷子

V(Chopstick[i])//放下筷子

P(seat)//哲学家进入餐厅申请位置

V(seat)//哲学家就餐完毕归还位置

第i(0<=i<=4)个哲学家“思考-就餐-思考过程”代码描述如下:

Philosopher(i)

{

While(TRUE)

{

Think();//哲学家思考

P(seat);

P(Chopstick[i]);

P(Chopstick[(i+1)mod 5]);

//哲学家拿起相邻筷子

Eat();

//哲学家就坐

V(Chopstick[i]);

V(Chopstick[i+1]);

V(seat);//就餐完毕,离席

}

}

 

2.2方案二:

分奇偶数编号进餐

奇数号哲学家先取左手的筷子,然后取右手的筷子,而偶数哲学家先取右手的筷子,然后再取左手的筷子,具体描述如下:

Semaphore mutex,chopstick[5];

mutex,value=4;

for(int i=0;i<5;i++)

   chopstick[i].value=1;

cobegin

   process philosopher_i()

{

  While(){

Think();//思考

P(mutex);

//最多允许4个哲学家申请筷子

P(chopstick[i]);

//拿起左手的筷子

P(chopstick[(i+1)%5];

//拿起右手的筷子

V(mutex);

//已拿到两个筷子,解除申请

Eat();//进餐

V(chopstick[i]);

//放回左手筷子

V(chopstick[(i+1)%5]);

//放回右手筷子

}

}

2.3方案三:

破坏死锁(请求)条件

(1)破坏请求保持条件

利用原子思想完成。即只有拿起两支筷子的哲学家才可以进餐,否则,一支筷子也不拿。

//利用AND机制实现第1位哲学家的活动描述为:

philosopher(int I)

{

while(true)

{

Think();

swait(chopstick[(I+1)]%5,chopstick[I]);

Eating();

Ssignal(chopstick[I],chopstick[(I+i)%5]);

}

}

//利用记录型信号量机制实现在初始化中增加一个信号量定义:semaphore mutex=1:

第1位哲学家的活动描述:

philosopher (int I)
  {

while(true)

{

Think();

wait(mutex);

wait(stiCk[I]);

wait(Stick[(I+1)%5]);

Signal(mutex);

Eating();

signal(stick[I]);

Signal(Stick[(I+1)%5]);
  }
 }

//该方法将拿两只筷子的过程作为临界资源,一次只允许一个哲学家进入

2.4方案四:

并发进程-多状态多信号量

   在方案二中,每次进餐总会有一位哲学家处在餐厅外面,未能让全部哲学家同时进入餐厅就坐,因此为每位哲学家设置三种状态,分别为:

(1) Think-思考状态

哲学家就餐完毕后释放手中的资源,并从就餐状态转为思考状态。

(2) Eating-就餐状态

   哲学家同时拿到左右两只筷子,由饥饿转为就餐状态。

(3) Hungry-饥饿状态

哲学家处于思考状态想要就餐但下未取得左右两只筷子时,由思考状态转为饥饿状态。

(1)方案中设置数组:     sate[i](0<=i<4)

每个元素对应一个哲学家,以便记录他们在生活过程中不同的状态

(2)设置有5个元素的信号量数组:

      s[i](0<=i<=4)

他们初值为0,每个元素对应一个哲学家。在拿不到筷子时,用它来与占用筷子的哲学家同步(即占用筷子的哲学家归还筷子,能唤醒受阻塞的哲学家)。

(3)为为方便每位哲学家判断其左右哲学家当前所处的状态,方案中设置两个变量:

LEFT=(i+1)mod5,(0<=i<=4)

RIGHT=(i+1)mod5,(0<=i<=4)

根据每个哲学家自己的编号,计算LEFT和RIGHT的值,就能得到左右相邻科学家的编号。例如:

第1个哲学家(编号为0)的左边是第5个哲学家(LEFT=4),右边的是第2个哲学家(RIGHT=1);

第2个哲学家(编号为1)的左边是第1个哲学家(LEFT=0),右边的是第3个哲学家(RIGHT=2);

......以此类推

为了保证每位哲学家在获取相邻者的状态和设置新状态,能互斥的进行,算法用到互斥信号量mutex,初值为1。

综合以上所述,第i(0<=i<=4)个哲学家的生活,由过程philosopher(i)给出,通过调用子程序,思考程序think(),获取筷子程序Take_chopstick(i);就餐程序 Eat();归还筷子程序Put_chopstick(i)来实现哲学家“思考-就餐-思考”的过程。描述如下:

Philosopher(i)

{

While(TRUE){

  Think();

  Take_chopstick(i);

  Eat();

  Put_chopstick(i);

}

}

//获取筷子Take_chopstick(i)的程序进入进入mutex临界区,最后将自己状态转换为HUNGRY,表示哲学家i饿了,注意此处仅仅表哲学家有就餐愿望,然后通过调用test(i)确定自己能否就餐,从test(i)返回后,现退出mutex,然后在与自己相邻的信号量s[i]上执行P操作。这个P操作或让哲学家i顺利就餐,或让哲学家在是s[i]上阻塞等待邻近的科学家放下筷子后被唤醒。描述如下:

Take_chopstick(i)

{

P(mutex);

State[i]=HUNGRY;

Test(i);

V(mutex);

*P(s[i]);

}

//归还筷子Put_chopstick(i)的程序进入进入mutex临界区,最后将自己状态转换为HUNGRY,表示哲学家i的此次就餐完毕,通过左右邻居的测试,决定是否需要把因拿不到筷子而阻塞的邻居唤醒,使其能够进餐。描述如下:

Put_chopstick(i);

{

 P(mutex);

  State[i]=HUNGRY;

Test(LEFT);

Test(RIGHT);

V(mutex);

}

//测试状态的子程序Test(i)的功能是,判定处在HUNGRY状态下的哲学家的左右邻居不在就餐状态时,把自己状态设置为EATING,然后在相应的同步信号量s[i]上执行V操作,以便在自己就餐完毕后,能够唤醒等待筷子就餐的左右邻居。描述如下:

Test(i)

{

if(state[i]==HUNGRY&&state[LEFT]!==E-ATING&&state[RIGHT]!=EATING)

{

State[i]=EATING;

*V(s[i];

}

}

2. 结语

哲学家进餐过程中,如果哲学家从来不交谈每个人都只顾着争夺自己左右两边的筷子时,就会产生死锁,每个哲学家都拿着左手的筷子,永远都在等右边的筷子(或者相反)。在实际的计算机问题中,缺乏筷子可以类比为缺乏共享资源。一种常用的计算机技术是临界资源使用,用来保证在某个时刻,资源只能被一个程序或一段代码访问。当一个程序想要使用的资源已经被另一个程序锁定,它就等待资源解锁。当多个程序涉及到都要同时申请使用临界资源时,就有可能发生死锁。并发进程执行过程中要进行同类资源竞争,引入PV操作和信号量,对并发进程执行进行条件控制,使得并发进程在在竞争同类资源时不会出现以上死锁或饥饿状态状况,根据资料文献整理有以下几个方法解决并发进程对临界资源的使用:限制允许就餐的哲学家数量为4个;设置信号量,利用PV操作互斥使用临界资源;通过对哲学家和临界资源编号,按奇偶数编号进行就餐;破坏导致死锁(请求和等待)的条件;为哲学家设置多状态,利用不同信号量去控制不同状态之间转换从而达到每个哲学家都可以顺利就餐的目的!

3.参考文献

[1]Dijkstra E W.Hierarchical or-dering ofsequential process[J].Acta information,1971,1(2):115-138

[2]胡元义,马骏宏操作系统组成原理[M]西安电子科技大学出版社.2014.

[3]李春华,徐明,周兴铭,多线程的软件实现[J].计算机工程与科学,1999,21(4):17-21

[4]付长冬,潘清,孟庆余.用户级线程[J].计算机工程与科学,1997,19(2):44-48.

[5]宗大华,宗涛,操作系统教程[M]人民邮电出版社.2008.

[6]侯海霞,李雪梅操作系统使用教程[M]机械工业出版社.2016

 
--------------------- 
作者:学习吧TOTORO 
来源:CSDN 
原文:https://blog.csdn.net/palm552233/article/details/80217982 
版权声明:本文为博主原创文章,转载请附上博文链接!

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值