管程
设计目的
- 将分散在各进程中的临界区集中起来管理
- 避免进程有意无意的违法同步操作
- 便于用高级语言来书写程序
定义
管程是由局部于自己的若干公共变量及其说明和所有访问这些公共变量的过程所组成的软件模块
形式结构
条件变量
条件变量是出现在管程内的一种数据结构,只能在管程中被访问,对管程内的所有过程是全局的,只能通过两个原语操作来控制它。
- wait©:阻塞调用进程并释放管程,直到另一个进程在条件变量c上执行signal
- signal©: 如果存在其他进程由于对条件变量c执行wait()而被阻塞,便释放对应进程;如果没有进程在等待,那么信号不被保存。
霍尔管程
可以注意到:当使用signal释放等待进程时,可能出现两个进程同时停留在管程内
霍尔管程:执行signal的进程等待,直到被释放进程退出管程或wait其他条件
实现
- 使用PV操作源于来实现对管程中过程的互斥调用,以及实现对共享资源互斥使用的管程
- 不要求signal操作时过程体的最后一个操作,切wait和signal操作可被设计成可中断的过程
数据结构
mutex
对每个管程,使用用于管程中过程互斥调用的信号量mutex(初值为1)
- 进程调用管程中任意过程时,应执行P(mutex)
- 进程退出管程时,需要判断是否有进程在next信号量等待
- 如果有(next_count>0),则通过V(next)唤醒一个发出signal的进程
- 否则应执行V(mutex)开放管程,让其他进程进入
- 为了使进程在等待资源期间(阻塞),其他进程能进入管程,所以在wait操作中也必须执行V(mutex),否则会妨碍其他进程无法进入管程,导致无法释放资源
next和next_count
对每个进程引入信号量next(初值为0)
- 凡发出signal操作的进程应该用P(next)阻塞自己,直到被释放进程退出管程或产生其他等待条件
- 进程退出管程前,需要检查是否有别的进程在信号量next上等待
- 若有(next_count>0),则用V(next)唤醒它
- next_count用于记录next上等待的进程个数
x_sem和x_count
引入信号量x_sem(初值为0)
- 申请资源得不到满足时,执行P(x_sem)阻塞
- x_count用于记录等待资源的进程数
- 执行signal操作时,应让等待资源的进程们中的某个进程立即恢复运行,而不是让其他进程抢先进入管程,用V(x_sem)实现
标准模板
习题
互斥问题
读者/写者问题
允许同读,同写互斥,读写互斥
type read-write=MONITOR{
int readcount=0,writecount=0;
semaphore R=0,W=0;
int R_count=0,W_count=0;
InterfaceModule IM;
define start_read,end_read,start_write,end_write;
use wait,signal,enter,leave;
}
void start_read(){ | void end_read(){
enter(IM); | enter(IM);
if(writecount>0)wait(R,R_count,IM); | readcount--;
readcount++; | if(readcount==0)signal(W,W_count,IM);
signal(R,R_count,IM); | leave(IM);
leave(IM); | }
} |
void start_write(){ | void end_write(){
enter(IM); | enter(IM);
if(readcount>0 || writecount>0) | writecount--;
wait(W,W_count,IM); | if(writecount==0)signal(R,R_count,IM);
writecount++; | else signal(W,W_count,IM);
leave(IM); | leave(IM);
} | }
cobegin
process Reader(){
read-write.start_read();
{读文件}
read-write.end_read();
}
process Writer(){
read-write.start_write();
{写文件}
read-write.end_write();
}
coend
哲学家就餐问题
五个人围成一圈,间隔放五把叉子,每个人需要两把叉子才能吃饭
type philosphers=MONITOR{
enum{thinking,eating,hungry} state[5]; //定义状态数组,枚举类型
semaphore ph[5]={0,0,0,0,0};
int ph_count[5]={0,0,0,0,0};
InterfaceModule IM;
define pickup,putdown,test;
use enter,leave,wait,signal;
}
void pickup(int i){
enter(IM);
state[i]=hungry;
test(i);
if(state[i]!=eating)wait(ph[i],ph_count[i],IM);
leave(IM);
}
void putdown(int i){
enter(IM);
state[i]=thinking;
test((i-1)%5);
test((i+1)%5);
leave(IM);
}
void test(int i){
if((state[(i-1)%5]!=eating)&&(state[i]==hungry)&&(state[(i+1)%5]!=eating)){
state[i]=eating;
signal(ph[i],ph_count[i],IM);
}
}
cobegin
process philosopher_i(int i){
while(true){
{thinking}
philosphers.pickup(i);
{eating}
philosphers.putdown(i)
}
}
coend
同步问题
生产者消费者问题
type pc=MONITOR{
item buffer[k]; //缓冲区结构
int putptr=0,getptr=0,count=0; //存取指针以及计数
semaphore full=0,empty=0;
int full_count=0,empty_count=0;
InterfaceModule IM;
define add,take;
use enter,leave,wait,signal;
}
void add(item &x){
enter(IM);
if(count==k)wait(empty,empty_count,IM); //缓冲区已满
item[putptr]=x;
putptr=(putptr+1)%k;
count++;
signal(full,full_count,IM); //唤醒等待的消费者
leave(IM);
}
void take(item &x){
enter(IM);
if(count==0)wait(full,full_count,IM);
x=item[getptr];
getptr=(getptr+1)%k;
count--;
signal(empty,empty_count,IM); //唤醒等待的生产者
leave(IM)
}
cobegin
process producer_i(){
item x;
{生产产品x}
pc.add(x);
}
process consumer_i(){
item x;
pc.take(x);
{消费产品x}
}
coend
苹果/橘子问题
父亲放苹果——女儿吃
母亲放橘子——儿子吃
type ao=MONITOR{
enum fruit{apple,orange} plate;
int count=0;
semaphore s=0,s1=0,s2=0; //s1:apple; s2:orange
int s_count=0,s1_count=0,s2_count=0;
define put,get;
use enter,leave,wait,signal;
}
void put(fruit f){
enter(IM);
if(count==0)wait(s,s_count,IM);
count=1;
plate=f;
if(f==apple)signal(s1,s1_count,IM); //可以取苹果了
else signal(s2,s2_count,IM); //可以取橘子了
leave(IM);
}
void get(fruit f,fruit &x){
enter(IM);
if(count==0 || plate!=f){
if(f==apple)wait(s1,s1_count,IM);
else wait(s2,s2_count,IM);
}
x=plate;
count=0;
signal(s,s_count,IM); //释放盘子信号量
leave(IM);
}
cobegin
process father(){ | process daughter(){
{准备苹果}; | ao.get(apple,x);
ao.put(apple); | {吃苹果}
} | }
process mother(){ | process son(){
{准备橘子}; | ao.get(orangex);
ao.put(orange); | {吃橘子}
} | }
coend