操作系统-PV原语(三)

一、哲学家进餐问题

二、读者-写者问题

三、举例说明


一、哲学家进餐问题

由Dijkstra提出并解决的哲学家进餐问题(The Dinning Philosophers Problem)是典型的同步问题

问题是描述有五个哲学家共用一张圆桌,分别坐在周围的五张椅子上,在圆桌上有五个碗和五只筷子,他们的生活方式是交替地进行思考和进餐。平时,一个哲学家进行思考,饥饿时便试图取用其左右最靠近他的筷子,只有在他拿到两只筷子时才能进餐。进餐毕,放下筷子继续思考。

1.利用记录型信号量解决哲学家解决问题

经分析可知,放在桌子上的筷子是临界资源,在一段时间内只允许一位哲学家使用。为了实现对筷子的互斥使用,可以用一个信号量表示一只筷子,由这五个信号量构成信号量数组。其描述如下:

semaphore chopstick[5]={1,1,1,1,1};    
所有的信号量均被初始化为1,
第i位哲学家的活动可描述为:
do{
    wait(chopstick[i]);
    wait(chopstick[(i+1)%5]);
    …
    //eat
    signal(chopstick[i]);
    signal(chopstick[(i+1)%5]);
    …
    //think
    …
}while(TRUE);

在以上描述中,当哲学家饥饿时,总是先去拿他左边的筷子,即执行wait(chopstick[i]);成功后,再去拿他右边的筷子,即执行wait(chopstick[(i+1)%5]);又成功后便可进餐。进餐毕,又先放下他左边的筷子,然后再放他右边的筷子。虽然,上述解法可保证不会有两个相邻的哲学家同时进餐,但却有可能引起死锁。假如五位哲学家同时饥饿而各自拿起左边的筷子时,就会使五个信号量chopstick均为0;当他们再试图去拿右边的筷子时,都将因无筷子可拿而无限期地等待。

对于这样的死锁问题,可采取以下几种解决方法

(1)至多只允许有四位哲学家同时去拿左边的筷子,最终能保证至少有一位哲学家能够进餐,并在用毕时能释放出他用过的两只筷子,从而使更多的哲学家能够进餐。

(2)仅当哲学家的左、右两只筷子均可用时,才允许他拿起筷子进餐。

(3)规定奇数号哲学家先拿他左边的筷子,然后再去拿右边的筷子;而偶数号哲学家则相反。按此规定,将是1、2号哲学家竞争1号筷子;3、4号筷子竞争3号筷子。即五位哲学家都先竞争奇数号筷子,获得后,再去竞争偶数号筷子,最后总会有一位哲学家能获得两只筷子而进餐。

2.利用AND信号量机制解决哲学家进餐问题

在哲学家进餐问题中,要求每个哲学家先获得两个临界资源(筷子)后方能进餐,这在本质上就是前面所介绍的AND同步问题。故用AND信号量机制可获得最简洁的解法。

semaphore chopstick[5]={1,1,1,1,1};    
do{
    …
    //think
    …
    Swait(chopstick[(i+1)%5],chopstick[i]);
    …
    //eat
    …
    Signal(chopstick[(i+1)%5],chopstick[i]);
}while(TRUE);

二、读者-写者问题

读者-写者(Reader-Writer Problem)问题是指保证一个Writer进程必须与其他进程互斥地访问共享对象的同步问题。读者-写者问题常被用来测试新同步原语。

1.利用记录型信号量解决读者-写者问题

为实现Reader与Writer进程间在读或写时的互斥而设置了一个互斥信号量wmutex。另外,再设置一个整型变量readcount表示正在读的进程数目。由于只要有一个Reader进程在读,便不允许Writer进程去写。因此,仅当readcount=0,表示尚无Reader进程在读时,Reader进程才需要执行wait(wmutex)操作。若wait(wmutex)操作成功,Reader进程便可去读,相应地,做readcount+1操作。同理,仅当Reader进程在执行了readcount减1操作后其值为0时,才须执行signal(wmutex)操作,以便让Writer进程写操作。又因为readcount是一个可被多个Reader进程访问的临界资源,因此,也应该为它设置一个互斥信号量rmutex。

读者-写者问题可描述如下:

semaphore rmutex=1,wmutex=1;
int readcount=0;
void reader(){
    do{
        wait(rmutex);
        if(readcount==0)
            wait(wmutex);
        readcount++;
        signal(rmutex);
        …
        perform read operation;
        …
        wait(rmutex);
        readcount--;
        if(readcount==0)
            signal(wmutex);
        signal(rmutex);
    }while(TRUE);
}
void Writer(){
     do{
        wait(wmutex);
        perform write operation;
        signal(wmutex);
     }while(TRUE);
}
void main(){
    cobegin
        Reader();
        Writer();
    coend
}

2.利用信号量集机制解决读者-写者问题

这里的读者-写者问题,与前面的略有不同,它增加了一个限制,即最多只允许RN个读者同时读。为此,又引入了一个信号量L,并赋予其值为RN,通过执行wait(L,1,1)操作来控制读者的数目,每当有一个读者进入时,就要先执行wait(L,1,1)操作,使L的值减1。当有RN个读者进入读后,L便减为0,第RN+1个读者要进入读时,必然会因wait(L,1,1)操作失败而阻塞。

对利用信号量集来解决读者-写者问题的描述如下:

int RN;
semphore L=RN,mx=1;
void Reader(){
    do{
        Swait(L,1,1);
        Swait(mx,1,0);
        …
        perform read operation;
        …
        Ssignal(L,1);
    }while(TRUE);
}
void Writer(){
    do{
        Swait(mx,1,1;L,RN,0);
        perform write operation;
        Ssignal(mx,1);
    }while(TRUE);
}
void main(){
    cobegin
        Reader();
        Writer();
    coend
}

其中,Swait(mx,1,0)语句起着开关的作用。只要无writer进程进入写操作,mx=1,reader进程就都可以进入读操作。但只要一旦有writer进程写操作时,其mx=0,则任何reader进程就都可以进入读操作。Swait(mx,1,1,L,RN,0)语句表示仅当既无writer进程在写操作(mx=1)、又无reader进程在读操作(L=RN)时,writer进程才能进入临界区进行写操作。

三、举例说明

1.桌子上有一空盘,允许放一只水果。爸爸可向盘中放苹果也可向盘中放桔子,儿子专等吃盘中的桔子,女人专等吃盘中的苹果。规定当盘空时一次只能放一只水果供吃者取用,请用P、V原语实现爸爸、儿子、女儿三个并发进程的同步。

分析:同步和互斥问题

1.考虑同步情况即所有的“等待情况”:

①爸爸要等待盘子为空;
②儿子要等待盘中水果为桔子;
③女儿要等待盘中水果为苹果。

2.互斥

看起来盘子好像是要作互斥处理的,但由于题目中的爸爸、儿子、女儿均只有一个,并且他们访问盘子的条件都不一样,所有他们根本不会同时去访问盘子,因此盘子也就不用作互斥处理了。

并发进程同步描述如下:

设置3个信号量,信号量Orange表示盘中有桔子,初值为0;信号量Apple表示盘中有苹果,初值为0;信号量EmptyDish表示盘子为空,初值为1。

int Orange=0,Apple=0,EmptyDish=1;
main(){
    cobegin
        father();
        son();
        daughter();
    coend
}
father(){
    while(1){
        P(EmptyDish);
        将水果放入盘中;
        if(放入的水果是桔子)
            V(Orange);
        else
            V(Apple);
    }
}
son(){
    while(1){
        P(Orange);
        从盘中取出桔子;
        V(EmptyDish);
        吃桔子;
    }
}
daughter(){
    while(1){
        P(Apple);
        从盘中取出苹果;
        V(EmptyDish);
        吃苹果;
    }
}

2.设公共汽车上,司机和售票员的活动分别是:

在汽车不断地到站、停车、行驶过程中,这两个活动有什么同步关系?用信号量和P、V操作实现他们的同步。

分析:

①确定进程间的同步关系。在汽车行驶过程中,售票员关车门后,向司机发出开车信号,司机接到开车信号后启动车辆;在汽车正常行驶过程中售票员售票,到站时司机停车,售票员在车停后开车门让乘客下车。
②确定信号量及其值。由于司机和售票员之间要互通消息,司机进程设置一个私有信号量run,用于判断司机能否进行工作,初值为0;售票员进程设置一个私有信号量stop,用于判断是否停车,售票员是否能够打开车门,初值为0。

同步描述如下:

int run=0,stop=0;
main(){
    parbegin
        driver();
        busman();
    parend
}
driver(){
    while(1){
        P(run);
        启动车辆;
        正常行车;
        到站停车;
        V(stop);
    }
}
busman(){
    while(1){
        关车门;
        V(run);
        售票;
        P(stop);
        开车门;
        下乘客;
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值