当前位置:   article > 正文

操作系统-经典同步例题_操作系统同步问题练习

操作系统同步问题练习

目录

1. 生产者/消费者问题

2. 读者和写者问题

3. 理发师睡觉问题


1. 生产者/消费者问题

描述:

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

基本流程:

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

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

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

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

示例1

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

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

分析

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

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

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

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

程序:

  1. semaphore mutex=1;
  2. semaphore orange=0,apple=0,empty=N;
  3. void father(){
  4. while(true){
  5. P(empty);
  6. P(mutex);
  7. //进入临界区,放置水果为result
  8. V(mutex)
  9. if (result == apple) V(apple);
  10. else V(orange);
  11. void son(){
  12. whlie(true){
  13. P(orange);
  14. p(mutex);
  15. //进入临界区,拿走橘子
  16. V(mutex);
  17. v(empty);
  18. }
  19. }
  20. void daughter(){
  21. while(true){
  22. P(apple);
  23. P(mutex);
  24. //进入临界区,拿走苹果
  25. V(mutex);
  26. V(empty);
  27. }
  28. }

示例2

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

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

分析:

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

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

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

代码:

  1. semaphore plate=1, apple=0, orange=0;
  2. void father(){
  3. while(true){
  4. P(plate);
  5. //放苹果
  6. V(apple)
  7. }
  8. }
  9. void mother(){
  10. while(true){
  11. P(plate);
  12. //放橘子
  13. V(orange);
  14. }
  15. }
  16. void son(){
  17. while(true){
  18. P(orange);
  19. //拿橘子
  20. V(plate);
  21. }
  22. }
  23. void daughter(){
  24. while(true){
  25. P(apple);
  26. //拿苹果
  27. V(plate);
  28. }
  29. }

例3

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

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

分析:

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

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

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

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

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

程序:

  1. semaphore empty_apple=1, empty_orange=1;
  2. semaphore apple=0, orange=0;
  3. void father(){
  4. while(true){
  5. P(empty_apple);
  6. //放苹果
  7. V(apple);
  8. }
  9. }
  10. void mother(){
  11. while(true){
  12. P(empty_orange);
  13. //放橘子
  14. V(orange);
  15. }
  16. }
  17. void dauther(){
  18. while(true){
  19. P(apple);
  20. P(orange);
  21. //同时拿走苹果和橘子
  22. V(orange);
  23. V(apple);
  24. }
  25. }

例4

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

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

分析:

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

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

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

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

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

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

程序:

  1. semaphore empty_dad = 1, empty_mom = 1;
  2. semaphore full_dad = 0, full_mom = 0;
  3. void daughter(){
  4. while(true){
  5. P(empty_dad);
  6. P(empty_mom)
  7. //作画
  8. V(full_dad);
  9. V(full_mom);
  10. }
  11. }
  12. void mother(){
  13. while(true){
  14. P(full_mom);
  15. //看画
  16. V(empty_mom);
  17. }
  18. }
  19. void father(){
  20. while(true){
  21. P(full_dad);
  22. //看画
  23. V(empty_dad);
  24. }
  25. }

2. 读者和写者问题

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

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

例1. 读者优先

描述:

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

信号量设置:

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

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

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

  1. semaphore wsem=1, x=1;
  2. int readcount=0;
  3. void reader(){
  4. while(true){
  5. P(x);
  6. readcount++;
  7. if (readcount == 1) P(wsem);
  8. V(x);
  9. //读数据
  10. P(x);
  11. readcount--;
  12. if (readcount == 0) V(wsem);
  13. V(x)
  14. }
  15. }
  16. void writer(){
  17. while(true){
  18. P(wsem);
  19. //写数据
  20. V(wsem)
  21. }
  22. }

例2. 公平读写

描述:

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

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

信号量设置:

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

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

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

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

程序:

  1. semaphore x=l, wrsem=1, wsem=l;
  2. int readcount=0
  3. void reader() {
  4. while (true) {
  5. P(wrsem);
  6. P(x);
  7. readercount++;
  8. if (readercount == 1) P(wsem);
  9. V(x);
  10. V(wrsem);
  11. //读数据;
  12. P(x);
  13. readercount--;
  14. if (readercount == 0) V(wsem);
  15. V(x);
  16. }
  17. }
  18. void writer(){
  19. while(true){
  20. P(wrsem);
  21. P(wsem);
  22. //写数据;
  23. V(wsem);
  24. V(wrsem);
  25. }
  26. }

例3 写者优先

描述:

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

信号量设置:

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

writecount:用于控制rsem信号量

y对变量writecount互斥算术操作

程序:

  1. int readcount = 0, writecount = 0;
  2. semaphore x=l, y= 1, wsem=1, rsem=l;
  3. void reader(){
  4. while(true){
  5. P(rsem);
  6. P(x);
  7. readcount++;
  8. if (readcount==1) P(wsem);
  9. V(x);
  10. V(rsem);
  11. //读数据
  12. P(x);
  13. readcount--;
  14. if (readcount==0) V(wsem);
  15. V(x);
  16. }
  17. }
  18. void writer(){
  19. while(true){
  20. P(y);
  21. writecount++;
  22. if (writecout==1) P(rsem);
  23. V(y);
  24. P(wsem);
  25. //写数据
  26. V(wsem);
  27. P(y);
  28. writecount--;
  29. if (writecount==0) V(rsem);
  30. V(y);
  31. }
  32. }

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

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

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

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

  1. int readcount = 0, writecount = 0;
  2. semaphore x=l, y= 1, wsem=1, rsem=l,z=1;
  3. void reader(){
  4. while(true){
  5. P(z)
  6. P(rsem);
  7. P(x);
  8. readcount++;
  9. if (readcount==1) P(wsem);
  10. V(x);
  11. V(rsem);
  12. V(z)
  13. //读数据
  14. P(x);
  15. readcount--;
  16. if (readcount==0) V(wsem);
  17. V(x);
  18. }
  19. }
  20. void writer(){
  21. while(true){
  22. P(y);
  23. writecount++;
  24. if (writecout==1) P(rsem);
  25. V(y);
  26. P(wsem);
  27. //写数据
  28. V(wsem);
  29. P(y);
  30. writecount--;
  31. if (writecount==0) V(rsem);
  32. V(y);
  33. }
  34. }

例4 过桥问题1

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

分析:  

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

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

桥-共享数据

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

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

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

程序:

  1. int countA=0, countB=0;
  2. semaphore mutex=1, xA=1, xB=1;
  3. void east_west(){
  4. while(1){
  5. P(xA)
  6. countA++;
  7. if (countA==1) P(mutex);
  8. V(xA);
  9. //过桥
  10. P(xA);
  11. countA--;
  12. if (countA==0) V(mutex);
  13. V(xA);
  14. }
  15. }
  16. void west_east(){
  17. while(1){
  18. P(xB);
  19. countB++;
  20. if (countB==1) P(mutex);
  21. V(xB);
  22. //
  23. P(xB);
  24. countB--;
  25. if (countB==0) V(mutex);
  26. V(xB)
  27. }
  28. }

例5 过桥问题2

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

分析:

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

  1. int countA=0, countB=0;
  2. semaphore mutex=1, xA=1, xB=1, count=4;
  3. void east_west(){
  4. while(1){
  5. P(xA)
  6. countA++;
  7. if (countA==1) P(mutex);
  8. V(xA);
  9. P(count)
  10. //过桥
  11. V(count)
  12. P(xA);
  13. countA--;
  14. if (countA==0) V(mutex);
  15. V(xA);
  16. }
  17. }
  18. void west_east(){
  19. while(1){
  20. P(xB);
  21. countB++;
  22. if (countB==1) P(mutex);
  23. V(xB);
  24. P(count)
  25. //过桥
  26. V(count)
  27. P(xB);
  28. countB--;
  29. if (countB==0) V(mutex);
  30. V(xB)
  31. }
  32. }

3. 理发师睡觉问题

描述:

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

分析:

  1. semaphore barber=1 //理发师是否就绪
  2. semaphore ready=0; //顾客是否就绪
  3. semaphore wait=5; //空椅子个数
  4. int num_customer=0; //店里顾客总人数,等于6时新顾客离开
  5. semaphore mutex_num=1 //对num_customer互斥访问
  6. semaphore finish=0; //理发师是否完成理发
  7. void customer(){
  8. while(true){
  9. p(mutex_num);
  10. if num_customer<6{
  11. num_customer++;
  12. V(mutex_num);
  13. P(wait); //找空椅子坐下
  14. P(barber); //找理发椅坐下
  15. V(wait); //释放空椅子
  16. V(ready); //告知理发师该顾客准备好了
  17. P(finish); //等待完成理发
  18. V(barber); //释放理发椅
  19. p(mutex_num);
  20. num_customer--;
  21. V(P_mutex);
  22. }
  23. else V(num_mutex) 离开;
  24. }
  25. }
  26. void Baber(){
  27. while(1){
  28. P(ready);
  29. //理发
  30. V(finish);
  31. }
  32. }

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

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

  1. semaphore mutex = 1; //互斥使用取号机的信号量
  2. semaphore empty = 10; //空座位的数量信号量
  3. semaphore full = 0; //已占座位的数量信号量
  4. semaphore service = 0; //等待叫号信号量
  5. void customer_i(){
  6. P(empty);
  7. P(mutex);
  8. //取号
  9. V(mutex);
  10. V(full);
  11. P(service);
  12. V(empty);
  13. //获取服务
  14. }
  15. void server(){
  16. while(1){
  17. P(full)
  18. V(serveice)
  19. //服务
  20. }
  21. }

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/不正经/article/detail/661569
推荐阅读
  

闽ICP备14008679号