Description
有五个哲学家绕着圆桌坐,每个哲学家面前有一盘面,两人之间有一支筷子,这样每个哲学家左右各有一支筷子。哲学家有2个状态,思考或者拿起筷子吃饭。如果哲学家拿到一只筷子,不能吃饭,直到拿到2只才能吃饭,并且一次只能拿起身边的一支筷子。一旦拿起便不会放下筷子直到把饭吃完,此时才把这双筷子放回原处。如果,很不幸地,每个哲学家拿起他或她左边的筷子,那么就没有人可以吃到饭了。
分析
该问题涉及Linux系统编程中经典的线程同步问题。
本篇采用信号量sem来实现。
信号量sem相当于进阶版的mutex,sem_wait()相当于sem–,sem_post相当于sem++。,定义多个信号量,其中筷子用初始化为1的sem来实现,保证其只能被一个哲学家拿起,要注意的是,为了防止四个哲学家同时拿起左手或者右手的筷子而导致死锁,我们需要定义一个sem数组,表示最多拿起左手或右手的哲学家数量为4,如此来避免死锁问题。
最后,利用srand函数和rand函数来保证哲学家拿起左右筷子的随机性。
代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/types.h>
#include <pthread.h>
#include <semaphore.h>
#define NUM 5 //5位哲学家
sem_t max[2]; //最多允许4位哲学家同时拿起左手或右手筷子(0为右手,1为左手)
sem_t chopsticks[NUM]; //信号量模拟筷子
void my_err(const char *str)
{
perror(str);
exit(1);
}
void *philosopher(void *arg)
{
int i = (long)arg;
int left = i; //左手筷子定义为i
int right = (i+1) % NUM; //右手筷子定义为i+1,若i == 4, right = 0
int first; //第一次是左手还是右手?
int second; //第二次?
int is_first_left;
while (1)
{
//思考时间
printf("-----哲学家%d思考中\n", i);
sleep(rand() % 5 + 1); //让他思考那么1-5s
//随机左右手
if (is_first_left = (rand() % 2))
{
first = left;
second = right;
}
else
{
first = right;
second = left;
}
//拿第一只筷子
sem_wait(&max[is_first_left]);
sem_wait(&chopsticks[first]);
if (is_first_left)
printf("=====第一次,哲学家%d拿起了左手的筷子%d\n", i, left);
else
printf("=====第一次,哲学家%d拿起了右手的筷子%d\n", i, right);
//第二只
sem_wait(&max[!is_first_left]);
sem_wait(&chopsticks[second]);
if (is_first_left)
printf("=====第二次,哲学家%d拿起了右手的筷子%d\n", i, right);
else
printf("=====第二次,哲学家%d拿起了左手的筷子%d\n", i, left);
//用餐时间
printf("-----哲学家%d用餐中\n", i);
sleep(rand() % 5 + 1);
//放筷子吧
sem_post(&chopsticks[left]);
sem_post(&max[1]);
printf("=====哲学家%d放下了左手的筷子%d\n", i, left);
sem_post(&chopsticks[right]);
sem_post(&max[0]);
printf("=====哲学家%d放下了右手的筷子%d\n", i, right);
}
}
int main(int argc, char *argv[])
{
srand( time(NULL) );
pthread_t pid[NUM];
//初始化
for (int i = 0; i < 2; i++)
sem_init(&max[i], 0, 4);
for (int i = 0; i < 5; i++)
{
sem_init(&chopsticks[i], 0, 1);
}
//创建哲学家
for (int i = 0; i < 5; i++)
{
pthread_create(&pid[i], NULL, philosopher, (void*)i);
}
//销毁
for (int i = 0; i < 5; i++)
{
pthread_join(pid[i], NULL);
sem_destroy(&chopsticks[i]);
}
for (int i = 0; i < 2; i++)
sem_destroy(&max[i]);
return 0;
}