死锁和饥饿-哲学家就餐问题

哲学家就餐问题

  • 背景:
  1. 假设5位哲学家住在一起(可以推广到n),每天生活就是思考和吃饭,每位哲学家需要2把叉子来吃意大利面.
  2. 就餐安排: 一张圆桌,5个板凳,5个盘子,5把叉子,每个想吃饭的哲学家做到椅子上,使用盘子2侧的叉子来吃面条,
  3. 问题: 设计算法 保证互斥(2位哲学家不能使用相邻的叉子),同时还要避免死锁(每个哲学家都在等待叉子,且占用了叉子)和饥饿

问题图片

信号量解决方案
  • 规定: 每位哲学家首先拿起左边的叉子,然后拿起右边的叉子
semaphore fork[5] = {1};
int i;
void philosopher (int i) {
    while (true) {
        think();
        wait(fork[i]); //等待左边的叉子
        wait(fork[(i+1) % 5]; // 等待右边的叉子
        eat();
        signal(fork[(i+1) % 5]); // 放回右边的叉子
        signal(fork[i]; //放回左边的叉子 (增加信号量)
    }
}
void mian() {
	parbegin(philosopher(0),philosopher(1).....philosopher(4));   
}
  • 此方案会导致死锁: 所有哲学家同时坐下来,都拿起左边的叉子,都去伸手拿右边的叉子,却都没有拿到

对上面死锁危险的解决方案:
  1. 再增加5把叉子,
  2. 哲学家学会只使用一把叉子吃面
  3. 增加服务员: 限制每次最多只能4个哲学家坐下来,那么至少有一个哲学家能够拿到2把叉子
增加服务员方案
  • 一个信号量room表示 : 限制每次哲学家同时坐下的数量
semaphore fork[5] = {1};
semaphore room = 4;
int i;
void philosopher (int i) {
	while (true) {
        think();
        wait(room);
        wait(fork[i]);
        wait(fork[(i+1) % 5]);
        eat();
        signal(fork[(i+1) % 5]);
        signal(fork[i]);
        signal(room);
    }
}
void main() {
    parbegin(philosopher(0),philosopher(1).....philosopher(4));
}
基于管程的解决方案
  1. 定义 5个管程的条件变量, 每把叉子对应一个条件变量,用来标识哲学家 等待 的叉子可用情况,

  2. 开一个bool数组记录每把叉子是否可用(true/false)

  3. 管程包含2个过程

    1. get_forks : 表示取哲学家左右的叉子, 如果至少有1把不可以用,那么在条件变量队列中等待,使得其他哲学家进程进入管程
    2. release_forks : 标识2把叉子可用
  4. 信号量方案类似,都是首先拿起左边的叉子,然后拿起右边的叉子

monitor dining_controller; // 声明管程
cron ForkReady[5];  //  管程的条件变量
bool fork[5] = {true}; // 标记叉子是否可用

void get_forks(int pid) { // pid既代表哲学家又代表叉子
    int left = pid;
    int right = (pid + 1) % 5;
    if (!fork[left]) // 左边叉子被使用,则等待条件变量
        cwait(ForkReady[left]);
    fork[left] = false;//左边叉子现在可以归我们用了,再次标记为不能用
    if (!fork[right])
        cwait(ForkReady[right]);
    fork[right] = false;
}

void release_forks(int pid) {
    int left = pid;
    int right= (pid + 1) % 5;
    if (empty(ForkReady[left])) // 没有哲学家在该队列上等待,则该叉子可以使用
        fork[left] = true;
    else //如果还有等待,增加信号量
        csignal(ForkReady[left]);
    /* 释放右边的叉子 */
    if (empty(ForkReady[right]))
        fork[right] = true;
    else
        csignal(ForkReady[right]);
}

void philosopher(int i) {
    while (true) {
        think();
        get_forks(i);
        eat();
        release_forks(i);
    }
}
void main() {
	parbegin(philosopher(0),philosopher(1).....philosopher(4));
}
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
操作系统哲学家就餐问题是计算机科学中一个经典的同步和互斥问题,涉及到多个哲学家(进程)如何共享资源(筷子)以避免死锁饥饿。在课程设计中,可以使用Java语言来实现这个问题的解决方案。 首先,课程可以从操作系统中的同步和互斥讲起,介绍临界区、互斥、同步、死锁等概念。然后引入哲学家就餐问题,通过讲解问题的背景和基本规则,让学生理解问题的复杂性和挑战。 接下来,可以使用Java的多线程编程来实现哲学家就餐问题解决方案。通过讲解Java中的线程、锁、条件变量等知识,指导学生如何在Java中设计哲学家就餐问题的模拟程序。学生可以学习如何定义哲学家和筷子的类,如何实现哲学家的行为规则以及如何保证资源的同步和互斥访问。 在课程的实践环节,学生可以动手实现哲学家就餐问题的Java程序,并进行调试和测试。他们需要考虑如何避免死锁饥饿的情况,保证程序的正确性和健壮性。通过实践,学生不仅可以加深对操作系统同步和互斥问题的理解,还可以提升他们的Java编程能力和解决问题的能力。 最后,在课程的总结中,可以讨论不同的解决方案和算法,以及它们的优缺点。通过这门课程的学习,学生可以对操作系统的原理有更深入的理解,同时也可以锻炼他们的编程和解决问题的能力。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值