理发师问题 - 信号量实现版
问题描述:
一个理发店由一个有几张椅子的等待室和一个放有一张理发椅的理发室组成。
1. 若没有要理发的顾客,则理发师去睡觉;
2. 若一顾客进入理发店,理发师正在为别人理发,且等待室有空椅子,则该顾客就找张椅子按顺序坐下;
3. 若一顾客进入理发店,理发师在睡觉,则叫醒理发师为该顾客理发;
4. 若一顾客进入理发店且所有椅子都被占用了,则该顾客就离开。
伪码实现:
引入3个信号量和一个控制变量:
1)控制变量waiting用来记录等候理发的顾客数,初值均为0;
2)信号量customers用来记录等候理发的顾客数,并用作阻塞理发师进程,初值为0;
3)信号量barbers用来记录正在等候顾客的理发师数,并用作阻塞顾客进程,初值为0(刚开始时理发师在睡觉,所以理发师这个资源数目为0);
4)信号量mutex用于互斥,初值为1.
关于p,v操作:
P操作可以看做是申请一个资源,不管这个资源有没有都将这个资源的数目减1,如果现在资源数目小于0,就阻塞。
v操作就是释放资源,先将这个资源的数目加1,如果发现这个资源数目小于等于0,就会唤醒在阻塞队列上的一个进程
看看PV原语实现:
1 顾客信号量 = 0 2 理发师信号量 = 0 3 互斥信号量mutex = 1 // 椅子是理发师和顾客精进程都可以访问的临界区 4 int 空椅子数量 = N //所有的椅子数量 5 6 理发师(线程/进程) 7 While(true){ //持续不断地循环 8 P(顾客) //试图为一位顾客服务,如果没有他就睡觉(进程阻塞) 9 P(互斥信号量) //如果有顾客,这时他被叫醒(理发师进程被唤醒),要修改空椅子的数量 10 空椅子数量++ //一张椅子空了出来 11 V(理发师) //现在有一个醒着的理发师,理发师准备理发,多个顾客可以竞争理发师互斥量,但是只有一个顾客进程可以被唤醒并得到服务 12 V(互斥信号量) //释放椅子互斥量,使得进店的顾客可以访问椅子的数量以决定是否进店等待 13 /* 理发师在理发 */ 14 } 15 16 17 顾客(线程/进程) 18 while(true) 19 { //持续不断地循环 20 P(互斥信号量) //想坐到一张椅子上 21 if (空椅子数量 > 0) 22 { //如果还有空着的椅子的话 23 空椅子数量-- //顾客坐到一张椅子上了 24 V(顾客) //通知理发师,有一位顾客来了 25 V(互斥信号量) //顾客已经坐在椅子上等待了,访问椅子结束,释放互斥量 26 P(理发师) //该这位顾客理发了,如果理发师还在忙,那么他就等着(顾客进程阻塞) 27 /* 竞争到了理发师则该顾客开始理发 */ 28 } 29 else 30 { //没有空着的椅子 31 V(互斥信号标) //不要忘记释放被锁定的椅子 32 /* 顾客没有理发就走了 */ 33 } 34 }
首先是信号量的实现版本:
1 #include <stdio.h> 2 #include <semaphore.h> 3 #include <pthread.h> 4 #define CHAIRS 3 5 6 //这个计数器才是共享资源 7 int waiting=0; //控制变量, 用来记录等候理发的顾客数 8 9 sem_t customers; //用来记录等候理发的顾客数,并用作阻塞理发师进程 10 sem_t barbers; //用来记录正在等候顾客的理发师数,并用作阻塞顾客进程 11 sem_t mutex; //用于线程互斥 12 13 void cut_hair() 14 { 15 printf("理发ing\n"); 16 } 17 18 void sleeping() 19 { 20 printf("sleeping\n"); 21 } 22 23 void *barber(void *arg) 24 { 25 while(1){ 26 sem_wait(&customers); //若无顾客,理发师睡眠 27 sem_wait(&mutex); 28 waiting--; 29 printf("理发师:等待顾客-1,还剩%d人等待 \n", waiting); 30 sem_post(&barbers); 31 sem_post(&mutex); 32 cut_hair(); //理发ing, 这个时候顾客已经独享理发师了,所以不在临界区 33 34 sem_wait(&mutex); 35 if(waiting==0){ //没人就长眠去呗 36 sem_post(&mutex); 37 break; 38 } 39 sem_post(&mutex); 40 } 41 pthread_exit(NULL); 42 } 43 44 void get_cut() 45 { 46 printf("顾客%u: 理发ing\n",(unsigned int)pthread_self()); 47 } 48 49 void *customer(void *arg) 50 { 51 sem_wait(&mutex); //互斥 52 if(waiting < CHAIRS){ //等候的人比椅子少 53 waiting++; //等候的人+1 54 sem_post(&customers); //多了一个顾客 55 sem_post(&mutex); 56 sem_wait(&barbers); //如果没有理发师了,那顾客就在椅子上等着 57 get_cut(); 58 } 59 else{ 60 printf("顾客%u: 没椅子了,走人 \n", (unsigned int)pthread_self()); 61 sem_post(&mutex); 62 } 63 sem_post(&mutex); //如果前面没有椅子了,就直接走了 64 pthread_exit(NULL); 65 } 66 67 68 69 int main(int argc, char const *argv[]) 70 { 71 void * retval; 72 int res=0; 73 int i; 74 sem_init(&customers, 0, 0); //没有顾客 75 sem_init(&barbers, 0, 0); //没有理发师,都在睡觉呢 76 sem_init(&mutex, 0, 1); //实现互斥 77 78 pthread_t bar, cus[6]; 79 res+=pthread_create(&bar, NULL, barber, NULL); 80 for(i=0; i<6; i++){ 81 res+=pthread_create(&cus[i], NULL, customer, NULL); 82 } 83 if(res!=0){ 84 printf("线程创建失败!\n"); 85 pthread_exit(NULL); 86 } 87 printf("线程创建成功\n"); 88 pthread_join(bar,&retval); 89 for(i=0; i<6; i++){ 90 pthread_join(cus[i],&retval); 91 } 92 return 0; 93 }