当前位置: 代码迷 >> 综合 >> 1226 哲学家进餐
  详细解决方案

1226 哲学家进餐

热度:21   发布时间:2024-02-01 08:39:13.0

题目描述:
5 个沉默寡言的哲学家围坐在圆桌前,每人面前一盘意面。叉子放在哲学家之间的桌面上。(5 个哲学家,5 根叉子)
所有的哲学家都只会在思考和进餐两种行为间交替。哲学家只有同时拿到左边和右边的叉子才能吃到面,而同一根叉子在同一时间只能被一个哲学家使用。每个哲学家吃完面后都需要把叉子放回桌面以供其他哲学家吃面。只要条件允许,哲学家可以拿起左边或者右边的叉子,但在没有同时拿到左右叉子时不能进食。
假设面的数量没有限制,哲学家也能随便吃,不需要考虑吃不吃得下。
设计一个进餐规则(并行算法)使得每个哲学家都不会挨饿;也就是说,在没有人知道别人什么时候想吃东西或思考的情况下,每个哲学家都可以在吃饭和思考之间一直交替下去。
在这里插入图片描述
哲学家从 0 到 4 按 顺时针 编号。请实现函数 void wantsToEat(philosopher, pickLeftFork, pickRightFork, eat, putLeftFork, putRightFork):

philosopher 哲学家的编号。
pickLeftFork 和 pickRightFork 表示拿起左边或右边的叉子。
eat 表示吃面。
putLeftFork 和 putRightFork 表示放下左边或右边的叉子。
由于哲学家不是在吃面就是在想着啥时候吃面,所以思考这个方法没有对应的回调。
给你 5 个线程,每个都代表一个哲学家,请你使用类的同一个对象来模拟这个过程。在最后一次调用结束之前,可能会为同一个哲学家多次调用该函数。

示例:
输入:n = 1
输出:[[4,2,1],[4,1,1],[0,1,1],[2,2,1],[2,1,1],[2,0,3],[2,1,2],[2,2,2],[4,0,3],[4,1,2],[0,2,1],[4,2,2],[3,2,1],[3,1,1],[0,0,3],[0,1,2],[0,2,2],[1,2,1],[1,1,1],[3,0,3],[3,1,2],[3,2,2],[1,0,3],[1,1,2],[1,2,2]]
解释:
n 表示每个哲学家需要进餐的次数。
输出数组描述了叉子的控制和进餐的调用,它的格式如下:
output[i] = [a, b, c] (3个整数)

  • a 哲学家编号。
  • b 指定叉子:{1 : 左边, 2 : 右边}.
  • c 指定行为:{1 : 拿起, 2 : 放下, 3 : 吃面}。
    如 [4,2,1] 表示 4 号哲学家拿起了右边的叉子。

提示:
1 <= n <= 60

方法1:使用5个互斥量来标识5个叉子
题目描述:
(1)使用5个互斥量,标识5个叉子,每个线程,既哲学家,只有同时能够获得两个互斥量,既叉子的情况下,才能够继续执行,既吃面;
(2)若是不能同时获得两个互斥量,则将已经获得的互斥量进行释放,然后再尝试同时获得两个互斥量;

#include<pthread.h>
class DiningPhilosophers {
public://标识五个叉子的互斥量pthread_mutex_t mutexs[5];DiningPhilosophers() {for(int i=0;i<5;++i)//对5个互斥量初始化pthread_mutex_init(mutexs+i,0);}void wantsToEat(int philosopher,function<void()> pickLeftFork,function<void()> pickRightFork,function<void()> eat,function<void()> putLeftFork,function<void()> putRightFork) {//当前哲学家需要获得的左右两个叉子int left_fork=philosopher;int right_fork=(philosopher+1)%5;//标识是否获得了左右两个叉子int get_left_fork=1;int get_right_fork=1;while(get_left_fork||get_right_fork){//若是某个叉子没有获得,则再次进入循环//先将之前获得叉子释放if(get_left_fork==0)pthread_mutex_unlock(mutexs+left_fork);if(get_right_fork==0)pthread_mutex_unlock(mutexs+right_fork);//再次尝试同时获得两个叉子get_left_fork= pthread_mutex_trylock(mutexs+left_fork);get_right_fork= pthread_mutex_trylock(mutexs+right_fork);}//跳出循环的时候,表示已经获得两个叉子pickLeftFork();pickRightFork();eat();putLeftFork();putRightFork();//执行完相关操作后,释放叉子pthread_mutex_unlock(mutexs+left_fork);pthread_mutex_unlock(mutexs+right_fork);}
};

方法2:使用互斥量和信号实现
主要思路:
(1)使用大小为5的数组,标识5个叉子,只有同时获得两个叉子的哲学家才能吃饭,既线程才能向下执行;
(2)使用互斥量实现对标识数组的共享,使用信号来通知数组的可获得状态;

#include<pthread.h>
class DiningPhilosophers {
public:pthread_mutex_t mutex;pthread_cond_t cond;bool is_using[5];//标识5个叉子DiningPhilosophers() {//初始化for(int i=0;i<5;++i)is_using[i]=false;pthread_mutex_init(&mutex,0);pthread_cond_init(&cond,0);}void wantsToEat(int philosopher,function<void()> pickLeftFork,function<void()> pickRightFork,function<void()> eat,function<void()> putLeftFork,function<void()> putRightFork) {//当前哲学家需要获得两个叉子编号int left_fork=philosopher;int right_fork=(philosopher+1)%5;//加锁,保证只有一个线程能获得标识叉子的数组pthread_mutex_lock(&mutex);//只有同时获得两个叉子,才能吃饭//既需要该线程在获得锁的时刻,两个叉子都没有被被其他线程使用while(is_using[left_fork]||is_using[right_fork]){pthread_cond_wait(&cond,&mutex);}//将标识两个叉子的数组元素的状态改变is_using[left_fork]=true;is_using[right_fork]=true;pthread_mutex_unlock(&mutex);//解锁,便于其他线程接着获取其他叉子//执行相关操作pickLeftFork();pickRightFork();eat();putLeftFork();putRightFork();//再次加锁,以获得对标识叉子的数组的独享,然后修改对应的叉子状态,既不再需要这个叉子pthread_mutex_lock(&mutex);is_using[left_fork]=false;is_using[right_fork]=false;pthread_mutex_unlock(&mutex);//解锁pthread_cond_signal(&cond);//通知释放了两个锁}
};