- 姓名 :万大明
- 学号 :201821121058
- 班级 :计算1812
1. 选择哪一个问题
-
哲学家进餐问题
有五个哲学家,他们的生活方式是交替地进行思考和进餐,哲学家们共用一张圆桌,分
别坐在周围的五张椅子上,在圆桌上有五个碗和五支筷子,平时哲学家进行思考,饥饿时
便试图取其左、右最靠近他的筷子,只有在他拿到两支筷子时才能进餐,该哲学家进餐完
毕后,放下左右两只筷子又继续思考。
图解:
约束条件:
(1)只有拿到两只筷子时,哲学家才能吃饭。
(2)如果筷子已被别人拿走,则必须等别人吃完之后才能拿到筷子。
(3)任一哲学家在自己未拿到两只筷子吃完饭前,不会放下手中已经拿到的筷子。
2. 给出伪代码
将取左筷子和右筷子做成一个原子操作, 所以哲学家拿筷子时只能拿完两个筷子(但是不能办到条件不满足时一个
筷子也不拿), 这种情况下整个餐桌最多只有一个哲学家在进行取筷子的操作, 一个哲学家取了一根筷子之后, 所有
哲学家都不能再取筷子, 但是可以放回筷子, 这样就保证了占用取筷子这一操作的哲学家至少能在另一根筷子使用
完毕后立即进餐. 从另一个方面来说, 这种原子操作限制的方法, 也可以认为是最多4人同时进餐的1人版.但是需要
注意这一做法的最坏情况, 即0号进餐时, 1号占用了旁边的一根筷子, 但是由于另一根筷子被0号占用, 所以进入阻
塞, 等0号用餐结束1号取得筷子进餐时, 2号有占用了一根筷子并且由于1号占用的筷子而进入阻塞, 依次类推, 则变
成了一个队列式的逐个运行. 在此情况下, 多线程丝毫没有发挥出并发的优势, 有着极大的浪费.
semaphore chopstick[5] = { 1, 1, 1, 1, 1 }; void philosopher(int i) { while (true) { think(); Swait(chopstick[i], chopstick[(i + 1) % 5]); eat(); Ssignal(chopstick[(i + 1) % 5], chopstick[i]); think(); } }
3. 给出完整代码
规定奇数号哲学家先拿左筷子再拿右筷子,而偶数号哲学家相反。所以将是 2,3 号哲
学家竞争 3 号筷子,4,5 号哲学家竞争 5 号筷子。1 号哲学家不需要竞争。最后总会
有一个哲学家能获得两支筷子而进餐。
#include <stdio.h> #include <stdlib.h> #include <malloc.h> #include <time.h> #include <unistd.h> #include <pthread.h> #include <semaphore.h> #define N 5 //五个哲学家 sem_t chopsticks[N]; //设置5种信号量,有5种不同类型的资源,每一种有1个,这样便于理解,因为每个哲学家需要的资源不同 int philosophers[N] = {0, 1, 2, 3, 4};//代表5个哲学家的编号,从0开始 void delay (int len) { int i = rand() % len; int x; while (i > 0) { x = rand() % len; while (x > 0) { x--; } i--; } } void *philosopher (void* arg) { int i = *(int *)arg; int left = i;//左筷子的编号和哲学家的编号相同 int right = (i + 1) % N;//右筷子的编号为哲学家编号+1 while (1) { if(i % 2 == 0){ printf("哲学家%d正在思考问题 ", i); delay(60000); printf("哲学家%d饿了 ", i); sem_wait(&chopsticks[right]);//此时这个哲学家左筷子的信号量-1之后>=0时,表示能继续执行。 printf("哲学家%d拿起了%d号筷子,现在只有一支筷子,不能进餐 ", i, right); sem_wait(&chopsticks[left]); printf("哲学家%d拿起了%d号筷子, 现在有两支筷子,开始进餐 ", i, left); delay(60000); sem_post(&chopsticks[left]); printf("哲学家%d放下了%d号筷子 ", i, right); sem_post(&chopsticks[right]); printf("哲学家%d放下了%d号筷子 ", i, left); } else{ printf("哲学家%d正在思考问题 ", i); delay(60000); printf("哲学家%d饿了 ", i); sem_wait(&chopsticks[left]);//此时这个哲学家左筷子的信号量-1之后>=0时,表示能继续执行。 printf("哲学家%d拿起了%d号筷子,现在只有一支筷子,不能进餐 ", i, left); sem_wait(&chopsticks[right]); printf("哲学家%d拿起了%d号筷子, 现在有两支筷子,开始进餐 ", i, right); delay(60000); sem_post(&chopsticks[left]); printf("哲学家%d放下了%d号筷子 ", i, right); sem_post(&chopsticks[right]); printf("哲学家%d放下了%d号筷子 ", i, left); } } } int main (int argc, char **argv) { srand(time(NULL)); pthread_t philo[N]; //信号量初始化 for (int i=0; i<N; i++) { sem_init(&chopsticks[i], 0, 1); } //创建线程 for (int i=0; i<N; i++) { pthread_create(&philo[i], NULL, philosopher, &philosophers[i]); } //挂起线程 for (int i=0; i<N; i++) { pthread_join(philo[i], NULL); } //销毁信号量 for (int i=0; i<N; i++) { sem_destroy(&chopsticks[i]); } return 0; }
4. 运行结果并解释
结果解释:可以看到,当哲学家两边的筷子都是空闲时,此哲学家可以进餐,当哲学家进餐玩完放下筷子
后进入思考状态,当前一个哲学家放下了筷子时,后一个哲学家才能在筷子无人使用的情况下进餐。