碎碎念:明天考试,今晚答疑才发现还要考这个(听老师的意思这里会考写伪代码使用信号量PV操作和管程解决经典IPC问题),速速搞一下。
下面进入正文
0 问题描述
理发店里有一位理发师,一把理发椅和N把供等候理发的顾客坐的椅子
- 如果没有顾客,则理发师便在理发椅上睡觉
- 当一个顾客到来时,他必须先唤醒理发师
- 如果顾客到来时理发师正在理发,则如果有空椅子,可坐下来等;否则离开
1 问题分析
先来分析一下涉及到的同步与互斥关系。
- 理发师与顾客
同步关系,理发师等待顾客来,顾客来了之后唤醒理发师。因此需要两个同步信号量。 - 顾客与椅子
互斥关系(竞争关系),因此需要互斥信号量来保证椅子数量准确。
2 解决方案
下面尝试用信号量(PV操作)和管程两种方式对问题进行解决。
2.1 PV操作
简单回顾一下PV操作,P操作是wait,同时对应-1;V操作是signal,同时对应+1。PV在同一个进程中为互斥信号量,分布在两个进程中为同步信号量。(很粗略的特征,但在实现时很好用)
因此,实现的伪代码如下:
semaphore customers = 0;
semaphore barbers = 0;
semaphore mutex = 1;
int waiting = 0; //等待的顾客数
int chairs = N; //总椅子数
process customer(){
P(mutex);
if(waiting < chairs){
waiting++;
V(customers); //唤醒等待顾客的理发师
V(mutex);
P(barbers); //等待理发师,有理发师则继续,没理发师则等待在该条件
get_haircut(); //得到理发
}
else{
V(mutex);
leave(); //离开
}
}
process barber(){
while(true){
P(customers); //等待顾客,有顾客则继续执行,否则等待在该条件
P(mutex);
waiting--;
V(barbers); //通知等待在该条件的顾客可以理发了
V(mutex);
cut_hair(); //理发
}
}
2.2 管程实现
其实管程就是对PV信号量的同一管理,即不需要再考虑互斥问题,管程自己有原子性,不可被中断。因此,只需要考虑wait和signal。管程中,进程会等待在条件变量上。本次实现设置了两个条件变量——barbers
和customers
,分别代表理发师已准备好(或者说是正在等待顾客的理发师),和顾客已准备好(等待理发师的顾客)。
对于顾客进理发店想要理发的过程即为:
- 有无椅子,没有则离开,有则执行下一步
- 等待人数加一
- 唤醒正在等顾客的理发师
barbers.signal()
- 如果理发师在忙,则坐在椅子上等,成为等待理发师的顾客
customers.wait()
对于理发师理发的过程:
- 等待顾客
barbers.wait()
- 询问是否有顾客来了(即唤醒等待理发师的顾客)
customers.signal()
- 等待人数减一
最后两个线程分别调用管程中的函数即可。
伪代码如下:
Monitor Barbershop {
int waiting = 0;
int chairs = N;
Condition barbers, costumers;
void cuthair(){
barbers.wait(); // 等待在理发师准备好的条件上
customers.signal(); // 通知准备好的顾客。理发师已ok
waiting--;
}
void gethaircut(){
if(waiting < chairs){
waiting++;
barbers.signal(); // 唤醒理发师,通知等待在准备好理发师条件的理发师。顾客已来
customers.wait(); // 如果理发师忙,等待在顾客准备好的条件上
} else {
// leave
}
}
}
void Barber() {
while(true){
// cut hair
Barbershop.cuthair();
cuthair();
}
}
void Customer() {
Barbershop.gethaircut();
// get hair cut
gethaircut();
}
以上即为睡眠的理发师的全部内容。