/* 哲学家问题,一般方法会有五个哲学家同时拿走右边的筷子,从而导致死锁 */#include<pthread.h>#include<stdio.h>#include<stdlib.h>#include<unistd.h>#include<pthread.h>#defineN5//总的哲学家的数量pthread_mutex_t mutex;pthread_mutex_t forks[5]={PTHREAD_MUTEX_INITIALIZER};typedefstructCthread{pthread_t thinkerID;int i;}thread;voidthinking(int id){printf("thinker %d is thinking!\n",id);}voideating(int id){printf("thinker %d is eating!\n",id);}voidtake_chopstick(int id){pthread_mutex_lock(&forks[(id+N-1)%5]);//哲学家右手筷子pthread_mutex_lock(&forks[(id+1)%5]);//哲学家左手筷子printf("the thinker %d take_chopsticks\n",id);return;}voidput_downcps(int id){printf("the thinker %d put_downcps\n",id);pthread_mutex_unlock(&forks[(id+N-1)%5]);pthread_mutex_unlock(&forks[(id+1)%5]);return;}void*thinker_work(void* arg){
thread *id =(thread*)arg;printf("thinker init[%d]\n",id->i);while(1){thinking(id->i);//pthread_mutex_lock(&mutex); //在这里添加互斥锁后,线程将一个一个进行工作,不会有多个线程同时取筷子而阻塞该进程,有效防止了死锁,但是其效率较低take_chopstick(id->i);//pthread_mutex_unlock(&mutex);eating(id->i);put_downcps(id->i);}}intmain(void){
thread *th =(thread *)malloc(5*sizeof(thread));for(int i =0;i <5; i++){
th[i].thinkerID =0;
th[i].i = i;}for(int i =0;i <5;i++){if(pthread_create(&(th[i].thinkerID),NULL,thinker_work,&th[i])<0)printf("created pthread failed\n");}for(int i =0;i<5;i++){pthread_join((th[i].thinkerID),NULL);}return0;}
方法一:单个执行
/* 哲学家问题,一般方法会有五个哲学家同时拿走右边的筷子,从而导致死锁
解决方案一:重新定义一个锁,在取筷子时加锁,这样取筷子的人数只有一个人,可以防止多个线程同时取筷而导致的死锁*/#include<pthread.h>#include<stdio.h>#include<stdlib.h>#include<unistd.h>#include<pthread.h>#defineN5//总的哲学家的数量pthread_mutex_t mutex;pthread_mutex_t forks[5]={PTHREAD_MUTEX_INITIALIZER};typedefstructCthread{pthread_t thinkerID;int i;}thread;voidthinking(int id){printf("thinker %d is thinking!\n",id);}voideating(int id){printf("thinker %d is eating!\n",id);}voidtake_chopstick(int id){pthread_mutex_lock(&forks[(id+N-1)%5]);//哲学家右手筷子pthread_mutex_lock(&forks[(id+1)%5]);//哲学家左手筷子printf("the thinker %d take_chopsticks\n",id);return;}voidput_downcps(int id){printf("the thinker %d put_downcps\n",id);pthread_mutex_unlock(&forks[(id+N-1)%5]);pthread_mutex_unlock(&forks[(id+1)%5]);return;}void*thinker_work(void* arg){
thread *id =(thread*)arg;printf("thinker init[%d]\n",id->i);while(1){thinking(id->i);pthread_mutex_lock(&mutex);//在这里添加互斥锁后,线程将一个一个进行工作,不会有多个线程同时取筷子而阻塞该进程,有效防止了死锁,但是其效率较低take_chopstick(id->i);pthread_mutex_unlock(&mutex);eating(id->i);put_downcps(id->i);}}intmain(void){
thread *th =(thread *)malloc(5*sizeof(thread));for(int i =0;i <5; i++){
th[i].thinkerID =0;
th[i].i = i;}for(int i =0;i <5;i++){if(pthread_create(&(th[i].thinkerID),NULL,thinker_work,&th[i])<0)printf("created pthread failed\n");}for(int i =0;i<5;i++){pthread_join((th[i].thinkerID),NULL);}return0;}
方法二:利用信号量控制线程数量
/* 哲学家问题,一般方法会有五个哲学家同时拿走右边的筷子,从而导致死锁
解决方案四:利用信号量来进行对线程数的控制*/#include<semaphore.h>#include<pthread.h>#include<stdio.h>#include<stdlib.h>#include<unistd.h>#include<pthread.h>#defineN5//总的哲学家的数量;
sem_t sem ;
pthread_mutex_t mutex;
pthread_mutex_t forks[5]={PTHREAD_MUTEX_INITIALIZER};typedefstructCthread{
pthread_t thinkerID;int i;}thread;voidthinking(int id){printf("thinker %d is thinking!\n",id);//sleep(2);}voideating(int id){printf("thinker %d is eating!\n",id);//sleep(3);}voidtake_chopstick(int id){pthread_mutex_lock(&forks[(id+N-1)%5]);//哲学家右手筷子pthread_mutex_lock(&forks[(id+1)%5]);//哲学家左手筷子printf("the thinker %d take_chopsticks\n",id);return;}voidput_downcps(int id){printf("the thinker %d put_downcps\n",id);pthread_mutex_unlock(&forks[(id+N-1)%5]);sem_post(&sem);pthread_mutex_unlock(&forks[(id+1)%5]);return;}void*thinker_work(void* arg){
thread *id =(thread*)arg;printf("thinker init[%d]\n",id->i);while(1){thinking(id->i);sem_wait(&sem);//利用信号量来控制线程的个数//pthread_mutex_lock(&mutex); //在这里添加互斥锁后,线程将一个一个进行工作,不会有多个线程同时取筷子而阻塞该进程,有效防止了死锁,但是其效率较低take_chopstick(id->i);//pthread_mutex_unlock(&mutex);eating(id->i);put_downcps(id->i);}}intmain(void){
thread *th =(thread *)malloc(5*sizeof(thread));sem_init(&sem,0,4);//初始化信号量for(int i =0;i <5; i++){
th[i].thinkerID =0;
th[i].i = i;}for(int i =0;i <5;i++){if(pthread_create(&(th[i].thinkerID),NULL,thinker_work,&th[i])<0)printf("created pthread failed\n");}for(int i =0;i<5;i++){pthread_join((th[i].thinkerID),NULL);}return0;}
方法三:利用回退机制进行操作
/* 哲学家拿筷子时每次都进行判断是否左手有筷子,如果左手有筷子则将其剥夺--基于回退机制解决哲学家问题 */#include<stdio.h>#include<stdlib.h>#include<unistd.h>#include<pthread.h>#include<errno.h>#defineN5//总的哲学家的数量pthread_mutex_t mutex;pthread_mutex_t forks[6]={PTHREAD_MUTEX_INITIALIZER};typedefstructCthread{pthread_t thinkerID;int i;}thread;voidthinking(int id){printf("thinker %d is thinking!\n",id);}voideating(int id){printf("thinker %d is eating!\n",id);}void*thinker_work(void* arg){
thread *id =(thread*)arg;int left,right;printf("thinker init[%d]\n",id->i);switch(id->i){case0:
left =5;
right =1;break;case1:
left =1;
right =2;break;case2:
left =2;
right =3;break;case3:
left =3;
right =4;break;case4:
left =4;
right =5;break;default:break;}while(1){thinking(id->i);sleep(2);//这里等待了两秒,所有线程都会创建好在这里睡眠pthread_mutex_lock(&forks[right]);//右手取到筷子printf("thinker %d carry the right chopstick\n",id->i);if(pthread_mutex_trylock(&forks[left])== EBUSY){pthread_mutex_unlock(&forks[right]);printf("unluckly ...\n");continue;}//pthread_mutex_lock(&forks[left]);printf("thinker %d carry chopstick %d\n",id->i,left);eating(id->i);sleep(2);//吃饭pthread_mutex_unlock(&forks[right]);//放下右边的筷子printf("thinker %d release the chopstick %d\n",id->i,right);pthread_mutex_unlock(&forks[left]);//放下左边的筷子printf("thinker %d release the chopstick %d\n",id->i,left);}}intmain(void){
thread *th =(thread *)malloc(5*sizeof(thread));for(int i =0;i <5; i++){
th[i].thinkerID =0;
th[i].i = i;}for(int i =0;i <5;i++){if(pthread_create(&(th[i].thinkerID),NULL,thinker_work,&th[i])<0)printf("created pthread failed\n");}for(int i =0;i<5;i++){pthread_join((th[i].thinkerID),NULL);}return0;}
方法四:对哲学家进行信号标记
/* 简化方案,不对筷子进行标记,而是对哲学家进行信号标记 */#include<pthread.h>#include<stdio.h>#include<stdlib.h>#include<unistd.h>#defineN5#defineLEFT(signo+N-1)%N#defineRIGHT(signo+1)%N#defineTHINK_TIME2#defineEAT_TIME1enum{EATING,HUNNGRY,THINKING} state[N];//用枚举对哲学家的状态进行初始化pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER,thinker[N];/* 这里对每个哲学家的左右都进行检测,如果左右两边哲学家都处于EATING状态,则自身放弃eating(解锁) */voidtest(int signo){printf("the thinker %d is trying to take the chopsticks\n",signo);if(state[signo]== HUNNGRY &&
state[LEFT]!= EATING &&
state[RIGHT]!= EATING ){
state[signo]= EATING;pthread_mutex_unlock(&thinker[signo]);}}voidtake_chopsticks(int signo){pthread_mutex_lock(&mutex);
state[signo]= HUNNGRY;test(signo);pthread_mutex_unlock(&mutex);pthread_mutex_lock(&thinker[signo]);}voidput_chopsticks(int signo){pthread_mutex_lock(&mutex);
state[signo]= THINKING;test(LEFT);test(RIGHT);pthread_mutex_unlock(&mutex);}voidthink(int signo){printf("the think %d is thinking\n",signo);sleep(THINK_TIME);}voideating(int signo){printf("the think %d is eating\n",signo);sleep(EAT_TIME);}void*work(void* argc){int i =*(int*)argc;while(1){think(i);take_chopsticks(i);eating(i);put_chopsticks(i);}}intmain(void){pthread_t th[N]={0};for(int i =0;i <5; i++)pthread_create(&th[i],NULL,work,(void*)(&i));for(int i =0;i <5; i++)pthread_join(th[i],NULL);return0;}
方法五:规定先例来防止死锁
/* 规定一个先例,偶数哲学家先左后右,奇数哲学家先右后左,这样总能保证一个哲学家先拿到筷子吃完,不会造成阻塞死锁 */#include<stdio.h>#include<stdlib.h>#include<pthread.h>#include<unistd.h>#defineN5pthread_mutex_t chopsticks[N]={PTHREAD_MUTEX_INITIALIZER};voideat(int i){printf("thinker %d eat eat eat\n",i);//sleep(2);}void*work(void*argc){int i =*(int*)argc;int left = i;int right =(i+1)%N;while(1){printf("哲学家%d正在思考问题\n", i);//sleep(2);printf("哲学家%d饿了\n", i);if(i %2==0){//偶数哲学家,先右后左pthread_mutex_lock(&chopsticks[right]);pthread_mutex_lock(&chopsticks[left]);eat(i);pthread_mutex_unlock(&chopsticks[left]);pthread_mutex_unlock(&chopsticks[right]);}else{//奇数哲学家,先左后又pthread_mutex_lock(&chopsticks[left]);pthread_mutex_lock(&chopsticks[right]);eat(i);pthread_mutex_unlock(&chopsticks[right]);pthread_mutex_unlock(&chopsticks[left]);}}}intmain(){pthread_t threadID[N];for(int i =0;i <5;i++)pthread_create(&threadID[i],NULL,work,(void*)(&i));for(int i =0;i<5;i++)pthread_join(threadID[i],NULL);}