PV----------理发师

来源:互联网 发布:国外社交软件 编辑:程序博客网 时间:2024/04/30 23:17

题目:

 假设有个理发店,只有一个理发师和N张可供顾客等待理发的椅子,如果没有顾客,则理发师睡觉;如果有一顾客进入理发店发现理发师在睡觉,则把他叫醒,试用信号量设计一个协调理发师和顾客的程序。

基本思想:

 

       睡眠理发师问题是一种同步问题的抽象描述,该问题主要的活动单元是理发师和顾客到来这两个。其中,理发师有理发和睡觉两个事件;顾客有到来,等待和离去三个事件。店里有固定的椅子数,上面坐着等待的顾客,顾客在到来这个事件时,需判断有没有空闲的椅子,理发师决定要理发或睡觉时,也要判断椅子上有没有顾客。所以,顾客和理发师之间的关系表现为:

 

             顾客—理发师之间的同步关系表现为

若无顾客,理发师便在理发椅上睡眠,等待。顾客到来时,先看等候的顾客数是否少于顾客椅子数,不是则离开,否则就留下来,声称要理发,并且叫理发师醒来或等理发师闲下来,再理发。

 

            顾客—理发师之间还有互斥关系:

由于等候的顾客数变量是临界资源,所以顾客进屋等待理发时对该变量进行的加1操作,以及理发师起身准备给顾客理发时对该变量进行的减1操作必须互斥。

 

因此,引入3个信号量和一个控制变量:

 

(1) 控制变量cust_wait    用来记录在椅子上等候理发的顾客数,由理发师线程和顾客线程共同访问,初值为0。

(2) 信号量sem_cust       用来记录等候理发的顾客数,并用作阻塞理发师线程,初值为0。

(3) 信号量sem_barber   用来记录正在等候顾客的理发师数,并用作阻塞顾客线程,初值为1。

(4) 信号量sem_mutex   用于互斥对临界变量cust_wait的访问,初值为1。

 

创建一个线程函数

pthread_create()用来创建一个线程,原型为:

int  pthread_create(pthread_t  *thread,  pthread_attr_t  *attr, void*(*start_routine)(void*),  void *arg);

thread:              该参数是一个指针,档线程创建成功时,用来返回创建的线程ID。

attr:                   该参数用于指定线程的属性,NULL表示使用默认属性。

start_routine:  该参数为一个函数指针,指向线程创建后要调用的函数。这个被线程调用的函数也被称为线程函数。

arg:                   该参数指向传递给线程函数的参数,函数不需要参数时,最后一个参数设为空指针。

 

当创建线程成功时,函数返回0,若不为0则说明创建线程失败,常见的错误返回代码为EAGAIN和EINVAL。前者表示系统限制创建新的线程,例如线程数目过多了;后者表示第二个参数代表的线程属性值非法。

创建线程成功后,新创建的线程则运行参数三和参数四确定的函数,原来的线程则继续运行下一行代码。

 

等待一个线程结束函数

pthread_join()用来等待一个线程的结束,函数原型为:

int pthread_join(pthread_tthread, void **retval); 

 

thread:     线程标识符,即线程ID,标识唯一线程。
retval:       用户定义的指针,用来存储被等待线程的返回值。

返回值:   0代表成功。 失败,返回的则是错误号。

功能:      以阻塞的方式等待thread指定的线程结束。当函数返回时,被等待线程的资源被收回。如果进程已经结束,那么该函数会立即返回。

 

 

注意:

1.一个可“join”的线程所占用的内存仅当有线程对其执行立pthread_join()后才会释放,因此为了避免内存泄漏,所有线程的终止时,要么已被设为DETACHED,要么使用pthread_join()来回收资源。

2.一个线程不能被多个线程等待,否则第一个接受到信号的线程成功返回,其余调用pthread_join()的线程返回错误代码ESRCH。

 

线程终止函数

linux下有两种方式可以使线程终止。第一种通过return从线程函数返回,第二种通过调用函数pthread_exit()是线程退出。

 

pthread_exit()用来退出线程,函数原型为:

void  pthread_exit(void  *retval)

     使用函数pthread_exit()退出线程,这是线程的主动行为;由于一个进程中的多个线程是共享数据段的,因此通常在线程退出之后,退出线程所占用的资源并不会随着线程的终止而得到释放,但是可以用pthread_join()函数来同步并释放资源。

 说明:

retval:pthread_exit()调用线程的返回值,可由其他函数如pthread_join()来检索获取。

 

 

信号量

信号量的数据类型为结构sem_t,它本质上是一个长整型的数。

函数sem_init()用来初始化一个信号量,它的原型为:

externint sem_init __P ((sem_t *__sem, int __pshared, unsigned int __value));

sem:          为指向信号量结构的一个指针。

pshared:   不为0时此信号量在进程间共享,否则只能为当前进程的所有线程共享。

value:       给出了信号量的初始值。

 

函数sem_post(sem_t *sem ):

用来增加信号量的值。当有线程阻塞在这个信号量上时,调用这个函数会使其中的一个线程不在阻塞,选择机制同样是由线程的调度策略决定的。

 

函数sem_wait(sem_t *sem ):

用来阻塞当前线程直到信号量sem的值大于0解除阻塞后将sem的值减一,表明公共资源经使用后减少。

 

 

伪码

 

                    理发师线程:                                                                                   顾客线程:

 

while(TURE)                                                                            P(sem_mutex);

{                                                                                              if(cust_wait<  N)

P(sem_cust);                                                                 {

P(sem_mutex);                                                                                 cust_wait++;

   cust_wait--;                                                                              V(sem_mutex);

  V(sem_mutex);                                                                         V(sem_cust);

理发                                                                                             P(sem_barber);

V(sem_barber);                                                                          被理发

}                                                                                             }

                                                                                               else

                                                                                              {

                                                                                                   V(sem_mutex);

                                                                                              }

  

 

源码

#include<stdio.h>#include<stdlib.h>#include<unistd.h>#include<pthread.h>#include<errno.h>#include<semaphore.h>#include<time.h>#define  CHAIR 5               //椅子数#define  MAX_CUST   10         //允许来理发的顾客数       int  cust_wait = 0;     //当前等待的顾客数       sem_t sem_cust;         //顾客信号量,阻塞理发师线程       sem_t sem_barber;       //理发师信号量,阻塞顾客线程       sem_t sem_mutex;        //定义互斥信号量    //理发师线程void *barber(void){          int i = 0;                      //理完发的人数          while(1)          {                 sem_wait(&sem_cust);         //(P操作) 如果没有顾客就睡觉               sem_wait(&sem_mutex);        //(P操作)申请互斥信号量               cust_wait--;                 //顾客理发,等待的顾客少一个               sem_post(&sem_mutex);        //(V操作)释放互斥信号量                 i++;               printf("我是第%d位顾客,现在我正在理发!\n",i);               sleep(5);                  //理发时间              sem_post(&sem_barber);          //(V操作)理完了,可以理下一个了              printf("\n我是第%d位顾客,我已经理完发了!\n",i);         }}//顾客线程void *cust(void *p){        int flag;                   //理发师是否空闲的标志位        int cust_count;             //当前来理发的顾客的数目          sem_wait(&sem_mutex);       //(P操作)申请互斥信号       cust_count = *(int *)p;       printf("\n******************************\n");       printf("**第%d位顾客进来了!         **",cust_count);       printf("\n******************************\n");       printf("\n我是第%d位顾客!\n",cust_count);       if(cust_wait < CHAIR)        //防止两个顾客坐同一个椅子       {            sem_getvalue(&sem_barber,&flag);       //获得信号量的值保存在flag中            cust_wait++;                          //等待的顾客多一个            if(cust_wait == 1 && flag == 1)       //如理发师空闲且只有一个人在等待,             {                                     //就直接叫醒理发师              printf("理发师还在睡觉,我得去叫醒他帮我理发\n");            }            else if(cust_wait > 0)         //否则在等待区等待            {                 if(cust_wait == 1)                     printf("此时就只有我在等待理发!\n");                 else                     printf("此时我加上另外%d位顾客一共还有%d个人在等待理发!\n",cust_wait-1,cust_wait);            }            sem_post(&sem_mutex);   //(V操作)释放互斥信号量            sem_post(&sem_cust);    //(V操作)要理发,叫醒理发师sem_wait(&sem_barber);  //(P操作)申请理发师为自己理发            sleep(1);       }       else       {          printf("没有空余的位置了!我不等了,走了算了!\n");          sem_post(&sem_mutex);  //如果没有座位,释放互斥信号量          pthread_exit(0);       //没有位子,顾客退出       }     pthread_exit(0);               //理完发,顾客退出  }//主函数int main(void){  int k;  pthread_t Pbarber;      //定义理发师线程  pthread_t Pcust[10];    //定义顾客线程  if(sem_init(&sem_cust,0,0) != 0)       //初始化顾客信号量为0  {       perror("顾客信号量初始化失败:\n");       return 0;  }  if(sem_init(&sem_barber,0,1) != 0)    //初始化理发师信号量为1,代表有一位理发师空闲  {       perror("理发师信号量初始化失败:\n");       return 0;  }  if(sem_init(&sem_mutex,0,1) != 0)      //初始化互斥信号量  {       perror("互斥信号量初始化失败:\n");       return 0;  }  if(pthread_create(&Pbarber,NULL,(void *)barber,NULL) != 0)   //创建理发师线程  {       perror("理发师有事来不了!不营业了!!!:\n");       return 0;  }  for(k = 1;k <= MAX_CUST;k++)          //创建顾客线程  {              if(pthread_create(&(Pcust[k-1]),NULL,(void *)cust,&k) != 0)            {             perror("有一个预约的顾客临时有事不能来了!  \n");             exit(0);        }        srand(time(0));        sleep(rand() % 3 + 1);    //1到3的随机数  }        for(k = 0;k < MAX_CUST;k++)         pthread_join(Pcust[k],NULL);             //挂起主线程,等待顾客线程退出         sleep(8);            //等待最后一位顾客理完发         return 0;}


 

 

 

 

 

 

windows

 

 

 

 

#include<stdio.h>#include<stdlib.h>#include<windows.h>#include<STDIO.H>#define  CHAIR 5            //椅子数#define  MAX_CUST   10      //允许来理发的顾客数       DWORD pbarber;       DWORD pcust[MAX_CUST];       int cust_count = 0;      //总顾客数       int  cust_wait = 0;    //当前等待的顾客数       HANDLE sem_cust;         //顾客信号量,阻塞理发师线程       HANDLE sem_barber;       //理发师信号量,阻塞顾客线程       HANDLE sem_mutex;        //定义互斥信号量    //理发师线程DWORD WINAPI barber(LPVOID lpParam){          int i = 0;                      //理完发的人数          while(1)          {               WaitForSingleObject(sem_cust,INFINITE);  //(P操作) 如果没有顾客就睡觉               WaitForSingleObject(sem_mutex,INFINITE);  //(P操作)申请互斥信号量               cust_wait--;                     //顾客理发,等待的顾客少一个               ReleaseMutex(sem_mutex);              //(V操作)释放互斥信号量                 i++;               printf("我是第%d位顾客,现在我正在理发!\n",i);               Sleep(5000);                  //理发时间              ReleaseSemaphore(sem_barber,1,NULL);      //(V操作)理完了,可以理下一个了              printf("\n我是第%d位顾客,我已经理完发了!\n",i);         }return 0;}//顾客线程DWORD WINAPI cust(LPVOID lpParam){        WaitForSingleObject(sem_mutex,INFINITE);         //(P操作)申请互斥信号        cust_count++;       printf("\n******************************\n");       printf("**第%d位顾客进来了!         **",cust_count);       printf("\n******************************\n");       printf("\n我是第%d位顾客!\n",cust_count);        if(cust_wait < CHAIR)        {           cust_wait++;           ReleaseMutex(sem_mutex);    //(V操作)释放互斥信号量           ReleaseSemaphore(sem_cust,1,NULL);       //(V操作)要理发,叫醒理发师           WaitForSingleObject(sem_barber,INFINITE);  //(P操作)申请理发师为自己理发            Sleep(1000);       }       else       {          printf("没有空余的位置了!我不等了,走了算了!\n");          ReleaseMutex(sem_mutex);   //如果没有座位,释放互斥信号量        } return 0;}//主函数int main(void){  int k;  HANDLE PVThread;  HANDLE PVThreads[MAX_CUST];  sem_cust = CreateSemaphore(NULL,0,MAX_CUST,NULL);  //初始化顾客信号量为0  sem_barber = CreateSemaphore(NULL,1,1,NULL);   //初始化理发师信号量为1,代表有一位理发师空闲  sem_mutex = CreateMutex(NULL,FALSE,NULL);      //初始化互斥信号量  PVThread = CreateThread(NULL,0,barber,NULL,0,&pbarber);  for(k = 0;k < MAX_CUST;k++)  {     PVThreads[k] = CreateThread(NULL,0,cust,NULL,0,&pcust[k]);       Sleep(2000);  }         Sleep(30000);            //等待最后一位顾客理完发         return 0;}


 

 

0 0
原创粉丝点击