经典的进程同步问题-----哲学家进餐问题详解
发布日期:2021-06-29 14:52:40 浏览次数:2 分类:技术文章

本文共 3828 字,大约阅读时间需要 12 分钟。

经典的进程同步问题-----哲学家进餐问题详解


一、问题描述

有五个哲学家围在一张圆桌,分别坐在周围的五张椅子上,在圆上有五个碗和木质筷子,他们的生活方式是交替的进行思考和进餐。平时,一个哲学家进行思考,,饥饿时便试图取用其左右最靠近他的筷子,只有在他拿到两支筷子时才能进餐。进餐完毕后,放下筷子继续思考。

我们可以得知,筷子是临界资源,同一根筷子同一时刻只能有一个哲学家可以拿到。

在这里插入图片描述

二、问题分析

由问题描述我们可以知道,一共有五个哲学家,也就是五个进程:

五只筷子,也就是五个临界资源;
因为哲学家想要进餐,必须要同时获得左边和右边的筷子,这就是要同时进入两个临界区(使用左右两个临界资源),才可以进餐。

因为是五只筷子为临界资源,因此设置五个信号量即可

在这里插入图片描述

三、 一个错误的例子

首先我们根据之前学习的知识来解决问题,根据上面的分析,我们先给出结论:

semaphore mutex[5] = {
1, 1, 1, 1, 1}; //初始化信号量void philosopher(int i){
do{
// thinking P(mutex[i]) // 判断左边的筷子是否可用 p(mutex[(i+1)%5]); // 判断右边的筷子是否可用 // ... // eat // ... V(mutex[i]); //退出临界区,放下左边的筷子 V(mutex[(i+1)%5]); //退出临界区,放下右边的筷子 }while(true);}

我们来分析下上面的代码,首先我们从一个哲学家的角度来看问题,程序似乎是没有问题的,申请到左右两支筷子后,然后开始进餐。

但是如果考虑到并发问题,五个哲学家同时拿起了左边的筷子,此时,五只筷子立刻都被占用了,没有可用的筷子了,当所有的哲学家再想拿起右边筷子的时候,因为临界资源不足,只能将自身阻塞,而所有的哲学家全部都会阻塞,并且不会释放自己手中拿着的左边的筷子,因此就会一直处于阻塞状态,无法进行进餐并思考。

​ 因为,为了解决五个哲学家争用的资源的问题,我们可以采用以下几种解决方法:

  1. 至多只允许有四位哲学家同时去拿左边的筷子,最终能保证至少有一位哲学家能够进餐,并在用餐完毕后能释放他占用的筷子,从而使别的哲学家能够进餐;
  2. 仅当哲学家的左、右两支筷子可用时,才允许他拿起筷子;
  3. 规定奇数号哲学家先拿他左边的筷子,然后再去拿右边的筷子;而偶数号哲学家则相反。

下面是我们对每种方法给出的伪代码。

四、 方法一:允许最多四位哲学家拿左边的筷子

对于方法一,至多只允许有四位哲学家同时拿左边的筷子,

我们可以简单的通过增加一个信号量实现,通过这个 信号量限定哲学家并发去进餐的数量。

semaphore mutex[5] = {
1, 1, 1, 1, 1};semaphore count = 4; // 最多允许四位哲学家同时进餐void philosopher(int i){
do{
//thinging P(count); // 判断是否超过四个 P(mutex[i]); // 判断缓冲池是否仍有空闲的缓冲区 P(mutex[(i+1)%5]); // 判断缓冲池是否仍有空闲的缓冲区 // ... // eat // ... V(mutex[i]); // 释放空闲的缓冲区 V(mutex[(i+1)%5]); // 释放空闲的缓冲区 V(count); }while(true);}

五、 方法二:使用AND型信号量,同时判断左右的筷子

第二种方法,也就是使用AND型信号量,同时对哲学家左右两边的筷子同时申请。

下面是伪代码:

semaphore mutex[5] = {
1, 1, 1, 1, 1}; // 初始化信号量void philosopher(int i){
do{
// thinking Swait(mutex[i], mutex[(i+1)%5]); // 进餐,同时判断左右的筷子 // ... // eat // ... Ssignal(mutex[i] , mutex[(i+1)%5]); // 进餐完毕,释放筷子 }while(true);}

六、 方法三:奇数拿左边筷子,偶数拿右边筷子

规定奇数号哲学家先拿他左边的筷子,然后再去拿右边的筷子;而偶数号哲学家则相反。

伪代码如下:

semaphore mutex[5] = {
1, 1, 1, 1, 1}; // 初始化信号量void philosopher(int i){
do{
// thinking if(i%2 == 1){
P(mutex[i]); // 左 P(mutex[(i+1)%5]); // 右 }else{
P(mutex[(i+1)%5]); // 右 P(mutex[i]); // 左 } // ... // eat // ... V(mutex[i]); // 释放空闲的缓冲区 V(mutex[(i+1)%5]); // 释放空闲的缓冲区 }while(true);}

七、 测试

这里我们通过Java模拟实现哲学家进餐问题,这里我们使用方法一(其他几种方式只需修改对应哲学家线程中的代码即可),下面是具体的代码:

import com.alibaba.fastjson.JSON;import lombok.extern.slf4j.Slf4j;import java.util.ArrayList;import java.util.List;import java.util.concurrent.Semaphore;/** * 学者进餐问题 */public class PhilosophersTest {
static final Semaphore count = new Semaphore(4); static final Semaphore[] mutex = {
new Semaphore(1), new Semaphore(1), new Semaphore(1), new Semaphore(1), new Semaphore(1)}; static class Philosopher extends Thread {
Philosopher(String name) {
super.setName(name); } @Override public void run() {
do {
try {
count.acquire(); Integer i = Integer.parseInt(super.getName()); mutex[i].acquire(); mutex[(i + 1) % 5].acquire(); log.info("哲学家【{}】号吃了通心粉!", i); mutex[i].release(); mutex[(i + 1) % 5].release(); count.release(); Thread.sleep(5000); } catch (InterruptedException e) {
log.error("哲学家执行时产生异常!"); } } while (true); } } public static void main(String[] args) {
Philosopher p0 = new Philosopher("0"); Philosopher p1 = new Philosopher("1"); Philosopher p2 = new Philosopher("2"); Philosopher p3 = new Philosopher("3"); Philosopher p4 = new Philosopher("4"); p0.start(); p1.start(); p2.start(); p3.start(); p4.start(); }}

在这里插入图片描述

转载地址:https://ciellee.blog.csdn.net/article/details/107482597 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:经典的进程同步问题-----读者-写者问题详解
下一篇:经典的进程同步问题-----生产者-消费者问题详解

发表评论

最新留言

表示我来过!
[***.240.166.169]2024年04月19日 18时59分55秒