哲学家就餐问题C语言实现(涉及多线程、信号量等)
1、实验原理
由Dijkstra提出并解决的哲学家进餐问题(The Dinning Philosophers Problem)是典型的同步问题。该问题是描述有五个哲学家共用一张圆桌,分别坐在周围的五张椅子上,在圆桌上有五个碗和五只筷子,他们的生活方式是交替地进行思考和进餐。平时,一个哲学家进行思考,饥饿时便试图取用其左右最靠近他的筷子,只有在他拿到两只筷子时才能进餐。进餐完毕,放下筷子继续思考。
2、实验内容
显示出每个哲学家的工作状态,如吃饭,思考。连续运行30次以上都未出现死锁现象。
3、代码部分
3.1 重要代码展示
重要代码展示:
(1)初始化int型数组ID[NUM]标识线程ID号,用于后续传参;初始化信号量数组sem_chopsticks[NUM]模拟五根筷子;初始化信号量sem_eaters=4,用来控制同时可拿起筷子的最大人数。
int ID[NUM]={0,1,2,3,4};
sem_t sem_chopsticks[NUM];
sem_t sem_eaters;
sem_init(&sem_eaters,0,NUM-1)
(2)线程的例程处理函数,先等待信号量sem_eaters,再等待信号量sem_chopsticks[pthread_id],这两个信号量不为零说明此时同时拿起筷子的人数小于4,且该哲学家编号相同的筷子(记为左筷子)可以被拿起,所以拿起左子,然后等待右筷子sem_chopsticks[pthread_id+1]信号量,拿到右筷子后便可以进餐,随机释放上述三个信号量。
void philosopher(void * ptid){
int pthread_id = *(int *)ptid%NUM;
printf("%d philosopher is thinking...\n",(int)pthread_id);
sem_wait(&sem_eaters);
sem_wait(&sem_chopsticks[pthread_id]);
printf("%d philosopher takes chopstick %d...\n",(int)pthread_id,(int)pthread_id);
sem_wait(&sem_chopsticks[(pthread_id+1)%NUM]);
printf("%d philosopher takes chopstick %d...\n",(int)pthread_id,((int)pthread_id+1)%NUM);
printf("%d philosopher is eating, %d philosopher had already dined.\n",(int)pthread_id,eaters_num);
sem_post(&sem_chopsticks[(pthread_id+1)%NUM]);
sem_post(&sem_chopsticks[pthread_id]);
sem_post(&sem_eaters);
eaters_num++;
printf("%d philosopher had dined, by now %d philosopher had already dined.\n",(int)pthread_id,eaters_num);}
(3)循环创建5个线程模拟五位哲学家,线程例子程函数为philosopher,并传入参数ID[i]标识线程的ID。
pthread_t philosopher_threads[NUM];
sem_signal_init();
for ( i= 0; i < NUM; i++) {
printf("%d times\n",i);
if (pthread_create(&philosopher_threads[i], NULL, (void *)&philosopher,&ID[i]) != 0){
perror("oops:pthread_create error!");
exit(1);
}
3.2 源码
#include <unistd.h>
#include "pthread.h"
#include "stdio.h"
#include "stdlib.h"
#include "semaphore.h"
#define NUM 5
int ID[NUM]={0,1,2,3,4};
sem_t sem_chopsticks[NUM];
sem_t sem_eaters;
int eaters_num=0;
void sem_signal_init(){
int i;
for (i=0; i<NUM; i++){
if (sem_init(&sem_chopsticks[i],0,1) == -1){
perror("oops:em_init error!");
exit(1);
}
}
if (sem_init(&sem_eaters,0,NUM-1) == -1){
perror("oops:em_init error!");
exit(1);
}
}
void philosopher(void * ptid){
int pthread_id = *(int *)ptid%NUM;
printf("%d philosopher is thinking...\n",(int)pthread_id);
sem_wait(&sem_eaters);
sem_wait(&sem_chopsticks[pthread_id]);
printf("%d philosopher takes chopstick %d...\n",(int)pthread_id,(int)pthread_id);
sem_wait(&sem_chopsticks[(pthread_id+1)%NUM]);
printf("%d philosopher takes chopstick %d...\n",(int)pthread_id,((int)pthread_id+1)%NUM);
printf("%d philosopher is eating, %d philosopher had already dined.\n",(int)pthread_id,eaters_num);
sem_post(&sem_chopsticks[(pthread_id+1)%NUM]);
sem_post(&sem_chopsticks[pthread_id]);
sem_post(&sem_eaters);
eaters_num++;
printf("%d philosopher had dined, by now %d philosopher had already dined.\n",(int)pthread_id,eaters_num);
}
int main(){
int i,l,j,k;
for (l = 0; l < 1000; ++l) {
printf("**********************%d times try ******************************",l+1);
pthread_t philosopher_threads[NUM];
sem_signal_init();
for ( i= 0; i < NUM; i++) {
printf("%d times\n",i);
if (pthread_create(&philosopher_threads[i], NULL, (void *)&philosopher,&ID[i]) != 0){
perror("oops:pthread_create error!");
exit(1);
}
}
for ( j = 0; j < NUM; j++) {
pthread_join(philosopher_threads[j], NULL);
}
sem_destroy(&sem_eaters);
for (k = 0; k < NUM ; ++k) {
sem_destroy(&sem_chopsticks[k]);
}
eaters_num = 0;
// sleep(2);
}
return 0;
}
3.3 运行结果展示
本程序用五个线程模拟五位哲学家,用五个sem_t型信号量模拟五根筷子(临界资源),解题的思想是:每次让每位哲学家优先拿起自己左边的筷子,并限制最多有四个人同时拿起筷子,从而避免了死锁情况,该程序模拟五个哲学家进餐一次的情况,并重复模拟了1000次以上来检验是否出现死锁情况,经检验程序运行正常,没有出现死锁状况,上述图片仅展示了第1次和第1000次的情况。