描述
一张圆桌上坐着5名哲学家,每两个哲学家之间的桌上摆一根筷子,桌子的中间是一碗米饭,如图2-10所示。哲学家们倾注毕生精力用于思考和进餐,哲学家在思考时,并不影响他人。只有当哲学家饥饿的时候,才试图拿起左、 右两根筷子(一根一根地拿起)。如果筷子已在他人手上,则需等待。饥饿的哲学家只有同时拿到了两根筷子才可以开始进餐,当进餐完毕后,放下筷子继续思考。分析
简单解法
相邻的哲学家是互斥关系,他们之间的筷子是共享资源。所以对筷子增加一个信号量用于控制。
void Pi(int id){//都去去拿筷子
while(enable){
sem_w(sem,id);
sem_w(sem,(id+1)%5);
eating(id);
sem_p(sem,id);
sem_p(sem,(id+1)%5);
sleep(TIMEOUT);
}
exit(0);
}
这里会存在问题就是可能一每一个哲学家都拿起一只筷子从而导致程序进入锁死状态。
解法一
利用一个单独的信号来控制去拿筷子的人数当只有4个人去拿筷子时就一定有一个哲学家能正常进餐。
void Pi1(int id){//都去去拿筷子
while(enable){
sem_w(room,0);
sem_w(sem,id);
sem_w(sem,(id+1)%5);
sem_p(room,0);
eating(id);
sem_p(sem,id);
sem_p(sem,(id+1)%5);
sleep(TIMEOUT);
}
exit(0);
}
解法二
每一次保证拿筷子的哲学家都能去拿到筷子吃饭。因此设置一个单独的控制信号保证每一次哲学家都能进餐。缺点是:每一次只能一个人去进餐。
void Pi2(int id){//都去去拿筷子
while(enable){
sem_w(room,0);
sem_w(sem,id);
sem_w(sem,(id+1)%5);
eating(id);
sem_p(sem,id);
sem_p(sem,(id+1)%5);
sem_p(room,0);
sleep(TIMEOUT);
}
exit(0);
}
解法三
规定奇数号的哲学家先拿起他左边的筷子,然后再去拿他右边的筷子;而偶数号 的哲学家则相反.按此规定,将是1,2号哲学家竞争1号筷子,3,4号哲学家竞争3号筷子.即 五个哲学家都竞争奇数号筷子,获得后,再去竞争偶数号筷子,最后总会有一个哲学家能获 得两支筷子而进餐。而申请不到的哲学家进入阻塞等待队列,根FIFO原则,则先申请的哲 学家会较先可以吃饭,因此不会出现饿死的哲学家
void Pi3(int id){//规定奇数号的拿左偶数号拿右
while(enable){
if(id%2) take_forks(id);
else take_forks_r(id);
eating(id);
if(id%2) put_down_forks(id);
else put_down_forks_r(id);
sleep(TIMEOUT);
}
exit(0);
}
进程实现代码(使用system V实现的代码有点多就不能直接放了):https://download.csdn.net/download/dog_dream/11116555
线程实现代码:
#include<stdio.h>
#include<errno.h>
#include<signal.h>
#include<unistd.h>
#include<sys/types.h>
#include<stdlib.h>
#include<string.h>
#include<sys/sem.h>
#include<sys/shm.h>
#include<unistd.h>
#include<sys/ipc.h>
#include<sys/wait.h>
#include <semaphore.h>
#include <fcntl.h>
#include <pthread.h>
#define SEM_NAME "."
#define per(msg) \
do { printf("this error is in %s\n",msg); exit(EXIT_FAILURE); } while (0) //错误处理宏
#define SIZE_MAX 1024
#define TIMEOUT 2
#define N 5
#define NROOM 1
int enable=1;
sem_t chopstick[5];
sem_t room;
void sig_quit(int sig)
{
enable=0;
return ;
}
void eating(int id){
sleep(TIMEOUT);
printf("philosopher[%d] is eating...\n", id);
}
void take_forks(int id){
//获取左右两边的筷子
printf("philosopher[%d] is take_forks...\n", id);
sem_wait(&chopstick[id]);
sem_wait(&chopstick[(id+1)%5]);
}
void take_forks_r(int id){
//先获取右在获取左
sem_wait(&chopstick[(id+1)%5]);
sem_wait(&chopstick[id]);
}
void put_down_forks(int id){
printf("philosopher[%d] is put_down_forks...\n", id);
sem_post(&chopstick[id]);
sem_post(&chopstick[(id+1)%5]);
}
void put_down_forks_r(int id){
printf("philosopher[%d] is put_down_forks...\n", id);
sem_post(&chopstick[(id+1)%5]);
sem_post(&chopstick[id]);
}
void* Pi1(void *arg){//只能四个人去拿筷子
int id = *(int*)arg;
while(enable){
sem_wait(&room);
take_forks(id);
sem_post(&room);
eating(id);
put_down_forks(id);
}
}
void* Pi2(void *arg){//左右两支筷子都能用时才能去拿
int id = *(int*)arg;
while(enable){
sem_wait(&room);
take_forks(id);
eating(id);
put_down_forks(id);
sem_post(&room);
}
}
void* Pi3(void *arg){//规定奇数号的拿左偶数号拿右
int id = *(int*)arg;
while(enable){
if(id%2) take_forks(id);
else take_forks_r(id);
eating(id);
if(id%2) put_down_forks(id);
else put_down_forks_r(id);
}
}
void init()
{
pthread_t phiTid[N];
int i;
int err;
puts("*********start**********");
for (i = 0; i < N; ++i)
{
if(sem_init(&chopstick[i], 0, 1) != 0)
{
printf("init forks error\n");
}
}
sem_init(&room, 0, NROOM);
for(i=0; i < N; ++i){
int *tm=(int*)malloc(sizeof(int));
*tm=i;
err = pthread_create(&phiTid[i], NULL, Pi2, (void*)tm); //这种情况生成的thread id是0,1,2,3,4
if (err != 0)
per("can't create process for reader\n");
}
signal(SIGINT,sig_quit);
signal(SIGTERM,sig_quit);
while(enable);
for(i=0; i < N; ++i){
pthread_join(phiTid[i], NULL);
}
for (i = 0; i < N; i++)
{
err = sem_destroy(&chopstick[i]);
if (err != 0)
{
per("can't destory semaphore\n");
}
}
return ;
}
int main(){
init();
exit(0);
return 0;
}