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;}
- PV----------理发师
- 【OS】PV操作-理发师问题-VC++多线程模拟实现
- 《理发师》
- 理发师
- 理发师问题
- 理发师问题
- 理发师悖论
- 理发师问题
- 理发师悖论
- 理发师问题
- 理发师问题
- 理发师问题
- 理发师该怎么说?
- 睡眠理发师问题
- 睡眠理发师问题
- 理发师悖论与集合
- 困睡的理发师
- 睡眠理发师问题
- EJB 2.0 ejb-jar.xml详解
- HttpServlet的方法
- ubuntu apache2 php 中文乱码
- [ecshop 支付接口 开发调试] ecshop 需要做一些支付接口,和接口升级,经常需要支付返回
- Understanding lvalues and rvalues in C and C++
- PV----------理发师
- 虚拟机保护技术浅谈
- Android Studio资源
- ArcGIS Server JS 开发在线新增要素同时记录添加人、添加时间出现的问题
- php---utf-8文件下生成csv文件
- 使用HttpURLConnection实现在android客户端和服务器之间传递对象
- 搭建中级注册码(网络验证码)系统教程
- 克鲁斯卡尔(Kruskal)算法求最小生成树
- jQuery中添加自定义或函数方法