Linux 多线程同步之哲学家用餐问题

来源:互联网 发布:mac怎么打出emoji 编辑:程序博客网 时间:2024/04/27 23:07

转载自:http://blog.csdn.net/acceptedxukai/article/details/8307247

问题描述:

有五个哲学家公用一张餐桌,分别坐在周围的五张椅子上,在餐桌上有五个碗和五只筷子,他们的生活方式是交替地进行思考和用餐。平时,一个哲学家进行思考,饥饿时便试图拿取其左右最靠近他的筷子,只有在他拿到两只筷子时才能进餐,进餐完毕,放下筷子继续思考。(计算机操作系统 第三版)

书上为代码:

[cpp] view plain copy
 print?
  1. Var chopstick: array[0,...,4] of semaphore;  
  2. repeat  
  3.     wait(chopstick[i]);  
  4.     wait(chopstick[(i+1)%5])  
  5.             .  
  6.             .  
  7.     eat:  
  8.             .  
  9.             .  
  10.     signal(chopstick[i])  
  11.     signal(opstick[(i+1)%5])  
  12.             .  
  13.             .  
  14.     think:  
  15. until false;  
这样当五个哲学家同时饥饿时而各自拿起左边的筷子是,就会使五个信号量chopstick均为0;当他们再试图去拿右边的筷子时,都将因没有筷子可拿而无限地等待。从而导致死锁。

本文解决死锁的策略是:只有当哲学家的左右两只筷子均可以用的时候,才允许其拿起筷子进餐,否则则该哲学家一直请求左边的筷子保持阻塞状态(左边的筷子信号量为1则直接占有,信号量为0则进入阻塞状态),右边的筷子若信号量为0,则非阻塞,进入等待。


详细代码:

[cpp] view plain copy
 print?
  1. /* 
  2.  * philosopher.c 
  3.  * 
  4.  *  Created on: 2012-12-17 
  5.  *      Author: xkey 
  6.  */  
  7.   
  8. #include <stdio.h>  
  9. #include <stdlib.h>  
  10. #include <unistd.h>  
  11. #include <pthread.h>  
  12. #include <semaphore.h>  
  13. #include <string.h>  
  14.   
  15. #define PEOPLE_NUM 5  
  16. #define THINKING 1  
  17. #define HUNGRY 2  
  18. #define EATING 3  
  19.   
  20. sem_t chopsticks[PEOPLE_NUM];  
  21. pthread_mutex_t mutex;  
  22.   
  23. void *philosopher(void *arg){  
  24.     int id = (int) arg;  
  25.   
  26.     int state = THINKING;  
  27.     int right = (id + 1) % PEOPLE_NUM;  
  28.     int left = (id + PEOPLE_NUM - 1) % PEOPLE_NUM;  
  29.     char ptrState[32];  
  30.   
  31.     while(1){  
  32.         switch(state){  
  33.         case THINKING:  
  34.             usleep(300);  
  35.             state = HUNGRY;  
  36.             strcpy(ptrState,"Thinking before eat");  
  37.             break;  
  38.         case HUNGRY:  
  39.             strcpy(ptrState,"Hungry");  
  40.             if(sem_wait(&chopsticks[left]) == 0){//阻塞状态  
  41.                 if(sem_trywait(&chopsticks[right]) == 0){//非阻塞  
  42.                     strcpy(ptrState,"I will Eating");  
  43.                     state = EATING;  
  44.                 }else{  
  45.                     state = THINKING;  
  46.                     strcpy(ptrState,"I have not chopsticks");  
  47.                     sem_post(&chopsticks[left]);//释放请求的得到的left筷子  
  48.                     printf("Philosopher right chopsticks is busy,right=%d,thread id is %d\n",right,id);  
  49.                 }  
  50.             }else{  
  51.                 printf("Philosopher left chopsticks is busy,left=%d,thread id is %d\n",left,id);//这句话由于上面被阻塞永远不会输出  
  52.             }  
  53.             break;  
  54.         case EATING:  
  55.             printf("Philosopher fetch left and right chopsticks: (%d,%d), threadid is %d\n",left,right,id);  
  56.             sem_post(&chopsticks[left]);  
  57.             sem_post(&chopsticks[right]);  
  58.             printf("Philosopher release left and right chopsticks: (%d,%d), threadid is %d\n",left,right,id);  
  59.             usleep(500);  
  60.             state = THINKING;  
  61.             strcpy(ptrState,"Thinking after eat");  
  62.             break;  
  63.         }  
  64.         pthread_mutex_lock(&mutex);  
  65.         printf("Philosopher is %s, thread id is %d\n",ptrState,id);  
  66.         pthread_mutex_unlock(&mutex);  
  67.         usleep(1000);  
  68.     }  
  69.   
  70.     pthread_exit((void*)0);  
  71. }  
  72.   
  73. int main(){  
  74.     pthread_t tid[PEOPLE_NUM];  
  75.     int i;  
  76.     pthread_mutex_init(&mutex,NULL);  
  77.     for(i = 0 ; i < PEOPLE_NUM ; i ++){  
  78.         sem_init(&chopsticks[i],0,1);  
  79.     }  
  80.     for(i = 0 ; i < PEOPLE_NUM ; i ++){  
  81.         pthread_create(&tid[i],NULL,philosopher,(void*)i);  
  82.     }  
  83.     for(i = 0 ; i < PEOPLE_NUM ; i ++){  
  84.         pthread_join(tid[i],NULL);  
  85.     }  
  86.     return 0;  
  87. }  

 gcc编译时,加上-lphread选项,例:gcc philosopher.c -lpthread

这里有个问题,也就是解决死锁的策略

if(sem_wait(&chopsticks[left]) == 0){//阻塞状态
if(sem_trywait(&chopsticks[right]) == 0){//非阻塞

可以考虑如果第二个if使用sem_wait会怎么样,是不是还能保证解决死锁,个人认为均用sem_wait就不需要写if语句了,这样应该会造成死锁;

另:如果两个if均使用sem_trywait会怎么样?

个人觉得这两个问题是本文应该思考的地方,本身哲学家进餐问题的实现不难,关键就是解决死锁的策略。


本文解决哲学家用餐问题使用的是信号量方法,该方法详解的Bolg地址见http://blog.csdn.net/wtz1985/article/details/3826291点击打开链接

另附两篇博文都是关于解决哲学家进餐问题的:

1、使用互斥量:http://blog.csdn.net/kongzhp/article/details/7487150

2、使用信号量(本文参照的方法,但是我看他写的全部是用sem_wait的):http://www.cnblogs.com/margincc/archive/2011/06/30/2094418.html


附:由于本人对Linux多线程研究不深,如有错误,欢迎指教,谢谢。


下面贴出上面代码的执行结果:

Philosopher is Thinking before eat, thread id is 4
Philosopher fetch left and right chopsticks: (0,2), threadid is 1
Philosopher release left and right chopsticks: (0,2), threadid is 1
Philosopher is Thinking after eat, thread id is 1
Philosopher right chopsticks is busy,right=0,thread id is 4
Philosopher is I have not chopsticks, thread id is 4
Philosopher is Thinking before eat, thread id is 1
Philosopher fetch left and right chopsticks: (4,1), threadid is 0
Philosopher release left and right chopsticks: (4,1), threadid is 0
Philosopher is Thinking before eat, thread id is 3
Philosopher is Thinking before eat, thread id is 4
Philosopher is I will Eating, thread id is 1
Philosopher is Thinking after eat, thread id is 0
Philosopher is Thinking before eat, thread id is 2
Philosopher right chopsticks is busy,right=0,thread id is 4
Philosopher is I have not chopsticks, thread id is 4
Philosopher fetch left and right chopsticks: (0,2), threadid is 1
Philosopher release left and right chopsticks: (0,2), threadid is 1
Philosopher is I will Eating, thread id is 3
Philosopher is I will Eating, thread id is 2
Philosopher is Thinking before eat, thread id is 0
Philosopher is Thinking after eat, thread id is 1
Philosopher is Thinking before eat, thread id is 4
Philosopher fetch left and right chopsticks: (2,4), threadid is 3
Philosopher release left and right chopsticks: (2,4), threadid is 3
Philosopher fetch left and right chopsticks: (1,3), threadid is 2
Philosopher release left and right chopsticks: (1,3), threadid is 2

Philosopher is I will Eating, thread id is 0
Philosopher is Thinking after eat, thread id is 3
Philosopher is Thinking before eat, thread id is 1
Philosopher is I will Eating, thread id is 4
Philosopher is Thinking after eat, thread id is 2
Philosopher fetch left and right chopsticks: (4,1), threadid is 0
Philosopher is Thinking before eat, thread id is 3
Philosopher fetch left and right chopsticks: (3,0), threadid is 4
Philosopher release left and right chopsticks: (3,0), threadid is 4
Philosopher release left and right chopsticks: (4,1), threadid is 0

可以看到,一开始线程4就被阻塞,原因就是右边的筷子0被线程1占有

红色部分,线程4一直不能同时得到左右两边的筷子

紫色部分,线程2左边的筷子1是空闲的,但是右边的筷子3之前已经被线程4所请求过,但是正是由于线程4不能得到其右边的筷子0,所以放弃了已经占有的其左边筷子3,所以线程2能够执行。

蓝色体现了多线程的同步执行,除此之外,发现线程4同时得到了左右两边的筷子3和0



0 0
原创粉丝点击