传送门:

进程同步与互斥——信号量(实现锁、条件变量)

进程同步与互斥——哲学家就餐问题源码实现(dining philosopher’s problem)

进程同步与互斥——读者/写者问题源码实现(reader-writer lock)

进程同步与互斥——生产者/消费者(有界缓冲区)问题源码实现

进程同步与互斥——吸烟者问题源码实现(cigarette smoker’s problem)

进程同步与互斥——理发师问题源码实现(sleeping barber problem)

进程同步与互斥——相关问题汇总(源码+伪代码)

哲学家就餐问题

哲学家就餐问题(dining philosopher’s problem)是一个著名的并发问题,它由Dijkstra提出来并解决。这个问题之所以出名,是因为它很有趣,引人入胜,但其实用性却不强。可是,它的名气让我们在这里必须讲。实际上,你可能会在面试中遇到这一问题,假如老师没有提过,导致你们没有通过面试,你们会责怪操作系统老师的。因此,我们这里会讨论这一问题。假如你们因为这个问题得到工作,可以向操作系统老师发感谢信,或者发一些股票期权。

这个问题的基本情况是:假定有5位“哲学家”围着一个圆桌。每两位哲学家之间有一把餐叉(一共5把)。哲学家有时要思考一会,不需要餐叉;有时又要就餐。而一位哲学家只有同时拿到了左手边和右手边的两把餐叉,才能吃到东西。关于餐叉的竞争以及随之而来的同步问题,就是我们在并发编程中研究它的原因。

下面是每个哲学家的基本循环:

while(1)
{think();getforks();eat();putfork();
}

关键的挑战就是如何实现getforks()和putforks()函数,保证没有死锁,没有哲学家饿死,并且并发度更高(尽可能让更多哲学家同时吃东西)。

根据Downey的解决方案,我们会用一些辅助函数,帮助构建解决方案。它们是:

int left(int p)  { return p; }
int right(int p) { return (p + 1) % 5;}

如果哲学家p希望用左手边的叉子,他们就调用left§。类似地,右手边的叉子就用right§。模运算解决了最后一个哲学家(p = 4)右手边叉子的编号问题,就是餐叉0。

我们需要一些信号量来解决这个问题。假设需要5个,每个餐叉一个:sem_t forks[5]。

有问题的解决方案

我们开始第一次尝试。假设我们把每个信号量(在fork数组中)都用1初始化。同时假设每个哲学家知道自己的编号(p)。我们可以写出getforks()和putforks()函数。

void getforks()
{sem_wait(forks[left(p)]);sem_wait(forks[right(p)]);
}void putforks()
{sem_post(forks[left(p)]);sem_post(forks[right(p)]);
}

这个(有问题的)解决方案背后的思路如下。为了拿到餐叉,我们依次获取每把餐叉的锁——先是左手边的,然后是右手边的。结束就餐时,释放掉锁。很简单,不是吗?但是,在这个例子中,简单是有问题的。你能看到问题吗?想一想。问题是死锁(deadlock)。假设每个哲学家都拿到了左手边的餐叉,他们每个都会阻塞住,并且一直等待另一个餐叉。具体来说,哲学家0拿到了餐叉0,哲学家1拿到了餐叉1,哲学家2拿到餐叉2,哲学家3拿到餐叉3,哲学家4拿到餐叉4。所有的餐叉都被占有了,所有的哲学家都阻塞着,并且等待另一个哲学家占有的餐叉。我们在后续章节会深入学习死锁,这里只要知道这个方案行不通就可以了。

一种方案:破除依赖

解决上述问题最简单的方法,就是修改某个或者某些哲学家的取餐叉顺序。事实上,Dijkstra自己也是这样解决的。具体来说,假定哲学家4(编写最大的一个)取餐叉的顺序不同。相应的代码如下:

void getforks()
{if(p == 4){sem_wait(forks[right(p)]);        sem_wait(forks[left(p)]);   }else{sem_wait(forks[left(p)]);sem_wait(forks[right(p)]);       }
}

因为最后一个哲学家会尝试先拿右手边的餐叉,然后拿左手边,所以不会出现每个哲学家都拿着一个餐叉,卡住等待另一个的情况,等待循环被打破了。想想这个方案的后果,让你自己相信它有效。

源码实现:

#include <semaphore.h>
#include <unistd.h>
#include <iostream>using namespace std;sem_t forks[5];void think(int i)
{cout << "第" << i << "个哲学家正在思考" << endl;/* sleep(1); */
}void eat(int i)
{cout << "第" << i << "个哲学家正在吃东西" << endl;/* sleep(2); */
}int left(int p) {return p;}
int right(int p) {return (p + 1) % 5;}void getforks(int i)
{sem_wait(&forks[left(i)]);cout << "第" << i << "个哲学家拿起左筷子" << endl;sem_wait(&forks[right(i)]);cout << "第" << i << "个哲学家拿起右筷子" << endl;
}void putforks(int i)
{sem_post(&forks[left(i)]);cout << "第" << i << "个哲学家放下左筷子" << endl;sem_post(&forks[right(i)]);cout << "第" << i << "个哲学家放下右筷子" << endl;
}void* philosopher_process_0(void* arg)
{int id = *(int*)arg;think(id);getforks(id);eat(id);putforks(id);pthread_exit(0);
}void* philosopher_process_1(void* arg)
{int id = *(int*)arg;think(id);getforks(id);eat(id);putforks(id);pthread_exit(0);
}void* philosopher_process_2(void* arg)
{int id = *(int*)arg;think(id);getforks(id);eat(id);putforks(id);pthread_exit(0);
}void* philosopher_process_3(void* arg)
{int id = *(int*)arg;think(id);getforks(id);eat(id);putforks(id);pthread_exit(0);
}void* philosopher_process_4(void* arg)
{int id = *(int*)arg;think(id);getforks(id);eat(id);putforks(id);pthread_exit(0);
}int main()
{for(int i = 0; i < 5; i++){sem_init(&forks[i], 0, 1);}pthread_t philosopher[5];int id[5] = {0, 1, 2, 3, 4};pthread_create(&philosopher[0], nullptr, philosopher_process_0, &id[0]);pthread_create(&philosopher[1], nullptr, philosopher_process_1, &id[1]);pthread_create(&philosopher[2], nullptr, philosopher_process_2, &id[2]);pthread_create(&philosopher[3], nullptr, philosopher_process_3, &id[3]);pthread_create(&philosopher[4], nullptr, philosopher_process_4, &id[4]);pthread_join(philosopher[0], nullptr);pthread_join(philosopher[1], nullptr);pthread_join(philosopher[2], nullptr);pthread_join(philosopher[3], nullptr);pthread_join(philosopher[4], nullptr);return 0;
}

触发死锁是概率事件,实际运行结果:

第4个哲学家正在思考
第4个哲学家拿起左筷子
第4个哲学家拿起右筷子
第4个哲学家正在吃东西
第4个哲学家放下左筷子
第4个哲学家放下右筷子
第3个哲学家正在思考
第3个哲学家拿起左筷子
第3个哲学家拿起右筷子
第3个哲学家正在吃东西
第3个哲学家放下左筷子
第3个哲学家放下右筷子
第2个哲学家正在思考
第2个哲学家拿起左筷子
第2个哲学家拿起右筷子
第2个哲学家正在吃东西
第2个哲学家放下左筷子
第2个哲学家放下右筷子
第1个哲学家正在思考
第1个哲学家拿起左筷子
第1个哲学家拿起右筷子
第1个哲学家正在吃东西
第1个哲学家放下左筷子
第1个哲学家放下右筷子
第0个哲学家正在思考
第0个哲学家拿起左筷子
第0个哲学家拿起右筷子
第0个哲学家正在吃东西
第0个哲学家放下左筷子
第0个哲学家放下右筷子

进程同步与互斥——哲学家就餐问题源码实现(dining philosopher’s problem)相关推荐

  1. Jaca集合(四)Vector集合底层源码分析

    Vector的基本介绍: (1)Vector类的定义说明:我们进入源码界面进行查看: public class Vector<E>extends AbstractList<E> ...

  2. 哲学家就餐问题--信号量和互斥量预防死锁

    哲学家就餐问题可以采取预防死锁的方案,就是使用互斥量和信号量锁定资源. 互斥量: 对资源进行锁定的意思就是说,当一个哲学家使用叉子的时候,他首先要先把叉子锁定,然后,拿起来.这个时候如果别的哲学家也来 ...

  3. 用信号量实现进程互斥示例和解决哲学家就餐问题

    用信号量实现进程互斥示例和解决哲学家就餐问题 参考文章: (1)用信号量实现进程互斥示例和解决哲学家就餐问题 (2)https://www.cnblogs.com/alantu2018/p/84731 ...

  4. Linux线程同步(三)---互斥锁源码分析

    先给自己打个广告,本人的微信公众号:嵌入式Linux江湖,主要关注嵌入式软件开发,股票基金定投,足球等等,希望大家多多关注,有问题可以直接留言给我,一定尽心尽力回答大家的问题. 一 源码分析 1.li ...

  5. 每行代码都带注释,带你看懂Go互斥锁的源码

    前言 当提到并发编程.多线程编程时,都会在第一时间想到锁,锁是并发编程中的同步原语,他可以保证多线程在访问同一片内存时不会出现竞争来保证并发安全:在Go语言中更推崇由channel通过通信的方式实现共 ...

  6. [含论文+开题报告+答辩PPT+源码等]SSM校园食堂点餐系统订餐就餐餐厅(已降重)

    博主介绍:✌在职Java研发工程师.专注于程序设计.源码分享.技术交流和毕业设计✌  公众号:[程序代做 源码分享] 免费源码获取.精品资源.面试题库等都给你

  7. 操作系统:第二章 进程管理3 - 进程同步与互斥

    本文已收录至 Github(MD-Notes),若博客中有图片打不开,可以来我的 Github 仓库:https://github.com/HanquanHq/MD-Notes,涵盖了互联网大厂面试必 ...

  8. java Thread sleep 和obj.wait,以及sychronized,minor源码

    sleep()方法是Thread类里面的,主要的意义就是让当前线程停止执行,让出cpu给其他的线程,但是不会释放对象锁资源以及监控的状态,当指定的时间到了之后又会自动恢复运行状态. wait()方法是 ...

  9. 操作系统实验四 进程同步与互斥

    一.实验目的: 掌握基本的同步与互斥算法,理解P,V操作. 理解生产者消费者模型,了解其它典型的同步互斥模型,如哲学家就餐.读者-写者模型等. 了解LINUX中进程同步互斥的实现方法,掌握相关函数的使 ...

最新文章

  1. JGG:COVID-19感染导致儿童上呼吸道和肠道菌群持续失衡
  2. Oracle 索引扫描的五种类型
  3. 你是一个有价值的产品经理吗?
  4. docker piwik
  5. mysql主从复制思考_Mysql主从复制(拓展博客文章分享及思考)
  6. Linux网络IO精华指南
  7. atcoder 076
  8. 学习过程中的一些细节
  9. 移动云亮相 2021 IDC 年度盛典 共话变革与赋能
  10. 百度终于升级空间的编辑器了
  11. 小程序解决方案 Westore - 组件、纯组件、插件开发
  12. iis服务器怎么限制运行asp文件,Win2008 r2 IIS7.5制定目录禁止执行脚本的方法
  13. 光影精灵安装ubuntu20.04安装显卡驱动外界显示屏
  14. Fingerprint 解锁流程
  15. 数据分析之MySQL(十二)账户管理
  16. 大话西游片尾曲-一生所爱(世界是巨大的枷锁,你不得不重复自己或是别人的生活)
  17. nacos启动后CPU使用率过高
  18. breakpoint断点调试
  19. Oracle Study之--Oracle 11g RAC添加节点错误
  20. dodo:人脸识别方法个人见解

热门文章

  1. CRC校验码原理及自动生成源码
  2. Single-Stage Multi-Person Pose Machines
  3. 截屏软件在截屏时窗口变大问题解决
  4. electron-layui搭建桌面应用
  5. IT新闻类上榜100家最受欢迎博客网站
  6. android定制之内置sdcard Volume ID及盘符修改
  7. win10桌面图标变成白色的解决方法
  8. 润歌互动通过聆讯:最高募资2亿港元 拟10月17日上市
  9. 你为什么总是谈不好恋爱?可能是因为这个原因
  10. 7-6 圆柱体类设计