操作系统-经典同步例题

目录

1. 生产者/消费者问题

2. 读者和写者问题

3. 理发师睡觉问题


1. 生产者/消费者问题

描述:

  • 一个或多个生产者产生数据并放入缓冲
  • 每次只能有一个消费者从缓冲区取数据(互斥)
  • 每次只能由一个生产者或消费者访问缓冲区(互斥)
  • 保证缓冲区满时,生产者不会往缓冲区中增加数据
  • 保证缓冲区空时,消费者不能从缓冲区中取走数据

基本流程:

注意1先申请资源信号量,再申请互斥信号量,顺序不能颠倒,否则死锁

注意2同一进程中的多个signal语句顺序颠倒对执行无影响,但先释放互斥再资源时性能更好

注意3:同一信号量的PV操作可以出现在不同进程中,但必须配对

注意4:在一个进程中,wait操作不一定就先于signal

示例1

一个生产者两个消费者被连接到大小为N的缓冲区

  • 桌子上有一只盘子,最多可以放入N(N>0)个水果。
  • 爸爸随机向盘中放入苹果或桔子。儿子只吃盘中的桔子,女儿只吃盘中的苹果
  • 只有盘子中水果数目小于N时,爸爸才可以向盘子中放水果;
  • 仅当盘子中有自己需要的水果时,儿子或女儿才可以从盘子中取出相应的水果;
  • 每次只能放入或取出一个水果,且不允许多人同时使用盘子。

分析

盘子是一互斥资源,故设置互斥信号量mutex

爸爸、儿子因为桔子的放入与取出而同步,设置产品资源信号量orange

爸爸、女儿因为苹果的放入与取出而同步,设置产品资源信号量apple

爸爸、儿子、女儿因为共享盘子,设置空间资源信号量empty

程序:

semaphore mutex=1;
semaphore orange=0,apple=0,empty=N;

void father(){
    while(true){
        P(empty);
        P(mutex);
        //进入临界区,放置水果为result
        V(mutex)
        if (result == apple) V(apple);
        else V(orange);

void son(){
    whlie(true){
        P(orange);
        p(mutex);
        //进入临界区,拿走橘子
        V(mutex);
        v(empty);
    }
}

void daughter(){
    while(true){
        P(apple);
        P(mutex);
        //进入临界区,拿走苹果
        V(mutex);
        V(empty);
    }
}

示例2

两个生产者两个消费者被连接到大小为1的缓冲区

  • 桌子上有一只盘子,爸爸负责向盘中放苹果,妈妈负责向盘中放桔子
  • 儿子只吃盘中的桔子,女儿只吃盘中的苹果。
  • 只有盘子为空时,爸爸或妈妈才可以向盘子中放入一个水果。
  • 仅当盘子中有自己需要的水果时,儿子或女儿才可以从盘子中取出相应的水果。

分析:

盘子是一互斥访问的空间资源,且缓冲大小为1,可只设置一个盘子资源信号量plate = 1

爸爸、女儿因为苹果的放入与取出而同步,设置产品资源信号量apple = 0;

妈妈、儿子因为桔子的放入与取出而同步,设置产品资源信号量orange = 0;

代码:

semaphore plate=1, apple=0, orange=0;

void father(){
    while(true){
        P(plate);
        //放苹果
        V(apple)
    }
}
void mother(){
    while(true){
        P(plate);
        //放橘子
        V(orange);
    }
}
void son(){
    while(true){
        P(orange);
        //拿橘子
        V(plate);
    }
}
void daughter(){
    while(true){
        P(apple);
        //拿苹果
        V(plate);
    }
}

例3

两个生产者一个消费者被连接到大小为2的缓冲区

  • 桌子上有一只盘子,最多可以放入2个水果,
  • 爸爸负责向盘中放苹果,妈妈负责向盘中放桔子,女儿负责取出并消费水果。放入者和取出者不允许同时使用盘子。
  • 当且仅当盘子中同时存在苹果和桔子时,女儿才从盘子中取出并消费水果。

分析:

盘子中是否可以放入苹果,设置空间资源信号量empty_apple

盘子中是否可以取出苹果,设置产品资源信号量apple

盘子中是否可以放入桔子,设置空间资源信号量empty_orange

盘子中是否可以取出桔子,设置产品资源信号量orange

题中只说到“放入者和取出者不允许同时使用盘子”,那两个放入者可以同时使用盘子。所以不需要对盘子额外设置生产者的互斥信号量

程序:

semaphore empty_apple=1, empty_orange=1;
semaphore apple=0, orange=0;

void father(){
    while(true){
        P(empty_apple);
        //放苹果
        V(apple);
    }
} 
void mother(){
    while(true){
        P(empty_orange);
        //放橘子
        V(orange);
    }
}
void dauther(){
    while(true){
        P(apple);
        P(orange);
        //同时拿走苹果和橘子
        V(orange);
        V(apple);
    }
}

例4

一个生产者两个消费者,一幅画要分别给两人看

  • 女儿负责画画,爸爸、妈妈负责欣赏。
  • 女儿在白板上画完一幅画后,请爸爸、妈妈均欣赏过一遍后,再创作新画,依次重复

分析:

题中说的是“均欣赏一遍后”,也没强调是“依次”欣赏,再结合实际可认为消费者之间不需要互斥(可以理解为,消费者只读,不互斥)

爸爸是否欣赏过,设置空间资源信号量empty_dad

爸爸是否可以欣赏,设置产品资源信号量full_dad

妈妈是否欣赏过,设置空间资源信号量empty_mom

妈妈是否可以欣赏,设置产品资源信号量full_mom

(其实对于爸爸妈妈是否可以访问也可以就只设置一个full=2的信号量就行)

程序:

semaphore empty_dad = 1, empty_mom = 1; 
semaphore full_dad = 0, full_mom = 0;   

void daughter(){
    while(true){
        P(empty_dad);
        P(empty_mom)
        //作画
        V(full_dad);
        V(full_mom);
    }
}
void mother(){
    while(true){
        P(full_mom);
        //看画
        V(empty_mom);
    }
}
void father(){
    while(true){
        P(full_dad);
        //看画
        V(empty_dad);
    }
}        

2. 读者和写者问题

如何判断一个问题是生产者/消费者问题,还是读者/写者问题?

  • 生产者/消费者问题:数据消费后就没有了;读者/写者问题:数据可多次读。
  • 生产者/消费者问题:消费者彼此互斥;读者/写者问题:读者可以同时读。
  • 生产者/消费者问题:生产者/消费者之间有互斥关系和同步关系;读者/写者问题:读者/写者之间有互斥关系

例1. 读者优先

描述:

  • 一旦有读者正在读数据,则允许随后的读者进入读数据。
  • 只有当全部读者退出,才允许写者进入写数据。
  • 导致写者饥饿

信号量设置:

wsem:互斥信号量,用于Writers间互斥,WritersReaders互斥

readcount:统计同时读数据的Readers个数

x:对变量readcount互斥算术操作

semaphore wsem=1, x=1;
int readcount=0;

void reader(){
    while(true){
        P(x);
        readcount++;
        if (readcount == 1) P(wsem);
        V(x);
        //读数据
        P(x);
        readcount--;
        if (readcount == 0) V(wsem);
        V(x)
    }
}

void writer(){
    while(true){
    P(wsem);
    //写数据
    V(wsem)
    }
}

例2. 公平读写

描述:

写过程中,若其它读者、写者到来,则按到达顺序处理

读过程中,若其他读者到来,直接进入临界区读数据

信号量设置:

wsem:互斥信号量,用于Writers间互斥,Reader互斥Writers

readcount:统计同时读数据的Readers个数

x:对变量readcount互斥算术操作

wrsem:互斥信号量,确定Writer Reader请求顺序

程序:

semaphore x=l, wrsem=1, wsem=l;
int readcount=0;

void reader() {
  while (true) {
    P(wrsem);
    P(x);
      readercount++;
      if (readercount == 1) P(wsem);
    V(x);
    V(wrsem);
    //读数据;
    P(x);
      readercount--;
      if (readercount == 0) V(wsem);
    V(x);
  }
}
void writer(){
    while(true){
        P(wrsem);
        P(wsem);
        //写数据;
        V(wsem);
        V(wrsem);
    }
}

例3 写者优先

描述:

  • 当至少有一个写者声明想写数据时,则不再允许的读者进入读数据。
  • 例如:(队列尾)WWRRW(),让三个W进程能优先于R进程写数据
  • 解决了写者饥饿问题,但降低了并发程度,系统的并发性能较差。

信号量设置:

rsem:互斥信号量,当至少有一个写者申请写数据时互斥新的读者进入读数据,第一个写者受rsem影响,一旦有第一个写者,后续写者不受rsem其影响。但是读者需要在rsem上排队。

writecount:用于控制rsem信号量

y对变量writecount互斥算术操作

程序:

int readcount = 0, writecount = 0;
semaphore x=l, y= 1, wsem=1, rsem=l;

void reader(){
    while(true){
        P(rsem);
        P(x);
        readcount++;
        if (readcount==1) P(wsem);
        V(x);
        V(rsem);
        //读数据
        P(x);
        readcount--;
        if (readcount==0) V(wsem);
        V(x);
    }
}
void writer(){
    while(true){
        P(y);
        writecount++;
        if (writecout==1) P(rsem);
        V(y);
        P(wsem);
        //写数据
        V(wsem);
        P(y);
        writecount--;
        if (writecount==0) V(rsem);
        V(y);    
    }
}

缺陷:WRRRR队列,W会等待长R队列

改进 : 增加z信号量,只对读者起作用

在rsem上不允许建造读进程的长队列,否则写进程不能跳过这个队列.

允许一个读进程在rsem上排队,其他读进程在信号量z上排队

int readcount = 0, writecount = 0;
semaphore x=l, y= 1, wsem=1, rsem=l,z=1;

void reader(){
    while(true){
        P(z)
        P(rsem);
        P(x);
        readcount++;
        if (readcount==1) P(wsem);
        V(x);
        V(rsem);
        V(z)
        //读数据
        P(x);
        readcount--;
        if (readcount==0) V(wsem);
        V(x);
    }
}
void writer(){
    while(true){
        P(y);
        writecount++;
        if (writecout==1) P(rsem);
        V(y);
        P(wsem);
        //写数据
        V(wsem);
        P(y);
        writecount--;
        if (writecount==0) V(rsem);
        V(y);    
    }
}

例4 过桥问题1

有一座东西方向的独木桥,同一方向的行人可连续过桥。当某一方向有行人过桥时,另一方向行人必须等待。桥上没有行人过桥时,任何一端的行人均可上桥。请用P、V操作来实现东西两端人过桥问题。    

分析:  

读者优先问题的变化,思想类似读者优先

两组读者,同一组之间可以同时读,不同组之间互斥,即只要有一组读者上桥,同组读者优先于另一组读者

桥-共享数据

murex:互斥信号量,用于两组读者互斥写者

countR:统计读者数目(同时在桥上的行人数目)

xR:对变量countR互斥算术操作

程序:

int countA=0, countB=0;
semaphore mutex=1, xA=1, xB=1;

void east_west(){
    while(1){
        P(xA)
        countA++;
        if (countA==1) P(mutex);
        V(xA);
        //过桥
        P(xA);
        countA--;
        if (countA==0) V(mutex);
        V(xA);
    }
}
void west_east(){
    while(1){
        P(xB);
        countB++;
        if (countB==1) P(mutex);
        V(xB);
        //
        P(xB);
        countB--;
        if (countB==0) V(mutex);
        V(xB)
    }
}

例5 过桥问题2

 有一座东西方向的独木桥,同一方向的行人可连续过桥。当某一方向有行人过桥时,另一方向行人必须等待。桥上没有行人过桥时,任何一端的行人均可上桥。出于安全考虑,独木桥的最大承重为4人,即同时位于桥上的行人数目不能超过4。请用P、V操作来实现东西两端人过桥问题

分析:

类似上题,需引入信号量count=4来统计桥上人数,在进入临界区前后对count进行P、V

int countA=0, countB=0;
semaphore mutex=1, xA=1, xB=1, count=4;

void east_west(){
    while(1){
        P(xA)
        countA++;
        if (countA==1) P(mutex);
        V(xA);
        P(count)
        //过桥
        V(count)
        P(xA);
        countA--;
        if (countA==0) V(mutex);
        V(xA);
    }
}
void west_east(){
    while(1){
        P(xB);
        countB++;
        if (countB==1) P(mutex);
        V(xB);
        P(count)  
        //过桥
        V(count)
        P(xB);
        countB--;
        if (countB==0) V(mutex);
        V(xB)
    }
}

3. 理发师睡觉问题

描述:

  • 理发店有一位理发师、一把理发椅和5把供等候理发的顾客坐的椅子。
  • 如果没有顾客,则理发师睡觉。
  • 当一个顾客到来时,若理发师在睡觉他必须叫醒理发师进行理发,
  • 如果顾客到来时理发师正在理发,则如果有空椅子可坐,他就坐下来等。如果没有空椅子,他就离开。

分析:


semaphore barber=1   //理发师是否就绪
semaphore ready=0;   //顾客是否就绪
semaphore wait=5;    //空椅子个数  
int num_customer=0;  //店里顾客总人数,等于6时新顾客离开
semaphore mutex_num=1 //对num_customer互斥访问 
semaphore finish=0;   //理发师是否完成理发
void customer(){
    while(true){
        p(mutex_num);
        if num_customer<6{
            num_customer++;
            V(mutex_num);
            P(wait);      //找空椅子坐下
            P(barber);    //找理发椅坐下
            V(wait);      //释放空椅子 
            V(ready);     //告知理发师该顾客准备好了
            P(finish);    //等待完成理发
            V(barber);    //释放理发椅
            p(mutex_num);  
            num_customer--;
            V(P_mutex);
        }
        else V(num_mutex) 离开;
    }
}

void Baber(){
    while(1){
        P(ready);
        //理发
        V(finish);
    }
}

补充:理发师睡觉类似问题

银行提供一个服务窗口和10个供顾客等待的座位。顾客到达银行时,若有空座位,则到取号机上领取一个号,等待叫号。取号机每次仅允许一位顾客使用。当营业员空闲时,通过叫号选取一位顾客,并为其服务

semaphore mutex = 1;              //互斥使用取号机的信号量
semaphore empty = 10;             //空座位的数量信号量
semaphore full = 0;               //已占座位的数量信号量
semaphore service = 0;            //等待叫号信号量

void customer_i(){
    P(empty);
    P(mutex);
    //取号
    V(mutex);
    V(full);
    P(service);
    V(empty);
    //获取服务
}
void server(){
    while(1){
        P(full)
        V(serveice)
        //服务  
    }     
}

 

  • 11
    点赞
  • 74
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我不会写BUG

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值