DJ2-5 哲学家进餐问题

目录

1. 问题描述

2. 问题分析

3. 利用记录型信号量解决哲学家就餐问题

4. 利用 AND 信号量解决哲学家就餐问题


1. 问题描述

有五个亚洲哲学家共用一张圆桌(怜师闭关中),他们的生活方式是交替地进行思考和就餐。在圆桌上有五根筷子,哲学家饥饿时便试图取用其左右最靠近她的筷子,只有在她拿到两只筷子时才能进餐。进餐毕,放下筷子继续思考。

2. 问题分析

每根筷子都是临界资源,在一段时间内只允许一位哲学家使用。因此,使用互斥型信号量来表示每根筷子。其描述如下:

semaphore chopstick[5] = {1, 1, 1, 1, 1};

考虑到 semaphore 的结构,实际应该写成以下形式:

semaphore chopstick[5];

for(int i = 0; i < 5; ++i)
    semaphore chopstick[i].value = 1;

3. 利用记录型信号量解决哲学家就餐问题

当哲学家饥饿时,总是先去拿她左边的筷子,执行成功后,再去拿她右边的筷子:

wait(chopstick[i]);         //拿起左筷子
wait(chopstick[(i+1)%5]);   //拿起右筷子

进餐毕,又先放下她左边的筷子,再放下她右边的筷子:

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

则第 i 位哲学家的活动可描述为:

do {
    wait(chopstick[i]);         //拿起左筷子
    wait(chopstick[(i+1)%5]);   //拿起右筷子
    
    //eat

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

    //think

} while(true);

但有可能引起死锁:假如五位哲学家同时饥饿而各自拿起左边的筷子时,就会使五个信号量 chopstick 均为 0;当她们再试图去拿右边的筷子时,都将因无筷子可拿而无限期地等待。

可采取以下几种解决方法:

1)至多允许有四位哲学家同时去拿左边的筷子,最终能保证至少有一位哲学家能够进餐,并在用餐完毕时能释放出她用过的两根筷子,从而使更多的哲学家能够进餐。

姐姐们同时拿起了左边的筷子,Leeseo 不能再拿筷子了。这时小圆还能拿起右边的筷子,并在用餐完毕时释放出她用过的两根筷子,从而使更多的哲学家能够进餐。

2)仅当哲学家的左、右两根筷子均可用时,才允许她拿起筷子进餐。

按照这种方法来思考,则将上图解释为安叮叮在和 Leeseo、Liz 争夺筷子的比赛中胜利,顺利拿到两根筷子;同理,秋天前辈也争夺到了两根筷子。

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

没人和安叮叮抢筷子,因此她顺利得到左筷子;Liz 和秋天前辈抢筷子;小圆和 Leeseo 抢筷子。假设秋天前辈和小圆抢到了筷子,则 Liz 和 Leeseo 由于没有抢到第一根筷子而退出比赛。安叮叮顺利拿到右筷子,秋天前辈和小圆再来争夺 4 号筷子。最终总有一位哲学家能获得两根筷子进餐。

当 Liz 和 Leeseo 均抢到时只会有一位哲学家能进餐,因为 Leeseo 的右筷子早被安叮叮拿了。

4. 利用 AND 信号量解决哲学家就餐问题

利用 AND 信号量就是上述方法中的第二种,即一次性将左右筷子都分配给哲学家,只要有一根筷子没有分配到,那另一根筷子也不分配给她。由于 Swait 和 Ssignal 操作是原子操作,所以率先去抢的哲学家一定能获得两根筷子。

semaphore chopstick[5] = {1, 1, 1, 1, 1};

do {
    Swait(chopstick[i], chopstick[(i+1)%5]);
    
    //eat

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

    //think

} while(true);

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
哲学家进餐问题是一个典型的并发编程问题,有多种解决方案,其中一种比较常见的方式就是使用信号量。 可以使用记录型信号量机制来解决哲学家进餐问题,避免死锁的情况。具体思路如下: 1. 定义5个记录型信号量 chopstick[5],表示5个筷子。 2. 每个哲学家进程有两个状态:thinking 和 hungry。 3. 当一个哲学家想要进餐时,将其状态设置为 hungry。 4. 当一个哲学家进入 hungry 状态时,首先尝试获取左边的筷子,如果左边的筷子被占用,则等待;如果左边的筷子空闲,则尝试获取右边的筷子,如果右边的筷子被占用,则释放左边的筷子,等待;如果右边的筷子也空闲,则将两个筷子标记为占用,将哲学家状态设置为 eating。 5. 当一个哲学家进餐结束后,将占用的两个筷子释放,将哲学家状态设置为 thinking。 6. 重复执行步骤3到5,直到所有哲学家进餐完毕。 下面是使用记录型信号量机制实现哲学家进餐问题的示例代码: ```c #include <stdio.h> #include <stdlib.h> #include <pthread.h> #include <semaphore.h> #include <unistd.h> #define N 5 // 哲学家的数量 #define LEFT (i + N - 1) % N // i 的左邻居 #define RIGHT (i + 1) % N // i 的右邻居 sem_t chopstick[N]; // 筷子信号量 void *philosopher(void *arg) { int i = *((int *) arg); while (1) { printf("Philosopher %d is thinking.\n", i); sleep(rand() % 5); // 随机思考时间 printf("Philosopher %d is hungry.\n", i); sem_wait(&chopstick[LEFT]); // 尝试获取左边的筷子 sem_wait(&chopstick[RIGHT]); // 尝试获取右边的筷子 printf("Philosopher %d is eating.\n", i); sleep(rand() % 5); // 随机进餐时间 sem_post(&chopstick[LEFT]); // 释放左边的筷子 sem_post(&chopstick[RIGHT]); // 释放右边的筷子 } } int main() { pthread_t tid[N]; int i, ret; for (i = 0; i < N; i++) { ret = sem_init(&chopstick[i], 0, 1); // 初始化所有筷子的信号量 if (ret != 0) { printf("Semaphore initialization failed.\n"); exit(1); } } for (i = 0; i < N; i++) { ret = pthread_create(&tid[i], NULL, philosopher, &i); // 创建哲学家进程 if (ret != 0) { printf("Thread creation failed.\n"); exit(1); } } for (i = 0; i < N; i++) { pthread_join(tid[i], NULL); // 等待所有哲学家进程结束 } return 0; } ``` 在上述代码中,使用了 sem_init 函数初始化所有筷子的信号量,使用 sem_wait 和 sem_post 函数分别尝试获取和释放筷子。在每个哲学家进程中,通过随机思考和进餐时间模拟哲学家的行为。需要注意的是,随机时间的使用可以避免所有哲学家同时进餐的情况,从而避免死锁的发生。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值