PV操作大题强化

image.png

1.生产者消费者问题——进程间关系为“生产资源-消费资源”

解题步骤

  1. 分析有几类进程——每类进程对应一个函数
  2. 在每一个函数内部可以用中文描述动作(如果动作只做一次,就不用加while循环,如果动作要重复,就要加while循环)
  3. 分析每一个动作在做之前,需不需要P操作**(很多动作要先获取某种资源)(注意隐含的互斥,比如缓冲区的访问,要加P(mutex))**比如去食堂吃饭要先打饭,只要有P操作,必有V操作
  4. 所有PV写完以后,再去定义信号量,定义完信号量后再去思考每一个信号量的初值为多少
  5. 检查多个P连续出现的地方,是否可能产生死锁**(可以尝试调整P操作顺序)。如果某一个信号量PV操作总是连续出现,中间没有夹杂其他P操作,则不可能因为此信号量而产生死锁(破坏了请求和保持条件)**
  6. 读题检查,是否符合题意
//不夹杂其余P操作
P(mutex);
动作;
V(mutex);
 
//夹杂其余P操作
P(mutex);
P(full);
动作;
V(empty);
V(mutex;
Semaphore rice=0;//米饭
Semaphore one=100;//碗	    然后把草稿中,米饭改为rice,碗改为one
同学(){		
  P(米饭);
  吃饭;
  V();

}


大师傅(){  
	while(1){
	P();
	打饭;
	V(米饭);
}

}


例题1 生产零件,组装产品问题

image.png


生产者消费者问题
有三个进程
互斥:如果生产工人把产品要访问货架F1,那么装配工人就不能访问货架F1了
互斥:如果货架放满了,就不能再放了 
Semaphore empty1=10;//F1还可以放多少A零件;
Semaphore full1=0;//F1已经放了多少A零件;
Semaphore empty2=10;//F2还可以放多少B零件
Semaphore full2=0;//F2已经放了多少B零件
Semaphore mutex1=1;//对货架F1互斥访问
Semaphore mutex2=1;//对货架F2互斥访问
A(){
  while(1){
  生产A零件;
  //如果要把A零件放到货架上,则要看F1是否还有空位,并且要互斥访问F1,因为装配工人每一次只能把一个零件放到一个货架上
  // F1和F2要互斥访问
  P(empty1);
  p(mutex1);
  把A零件放到货架F1;
  V(full1);
  V(mutex1);
}
}


B(){
  while(1){
    生产B零件;
    //如果要把B零件放到货架上,则要看F2是否还有空位,而且要互斥访问F2,因为装配工人每一次只能把一个零件放到一个货架上
    // F1和F2要互斥访问
    P(empty2);
    p(mutex2);
    把B零件放到货架F2;
    V(full2);
    V(mutex2);
  }
}



装配车间(){
  while(1){
    //要申请A,要互斥访问F1
    P(full1);
    P(mutex1);
    从货架F1取出一个A零件;
    V(mutex1);
    V(empty1);
    //申请B零件,互斥访问F2
    P(full2);
    P(mutex2);
    从货架F2取出一个B零件;
    V(empty2);
    V(mutex2);
    把A零件和B零件组合成一个产品;
  }
}








标准答案
image.png

例题2 和尚取水问题

image.png

互斥访问:,井
而且去缸里面取水的时候,要看缸里面还有没有水
小和尚相当于生产者   老和尚相当于消费者
老和尚也要去缸里面拿桶取水,然后饮水

Semaphore gang=1;//互斥访问缸;
Semaphore jing=1;//互斥访问井;
Semaphore empty=10;//缸中还能提多少水
Semaphore full=0;//已经提了多少水
Semaphore tong=3;//桶
小和尚(){
  while(1){
    
	//从井中提水首先要有桶 
  P(empty);//是否还能继续提水 
  P(tong);    //提水前要申请桶资源 P(); 
	P(jing);
    
    
  从井中提水;
  V(jing);
    
  //互斥访问缸  
  //如果缸中水满了,就不能继续提水
 
  P(gang); 	//提水入缸
    
  提水入缸;
  V(gang);  
  V(tong);
  V(full);
	}
}

老和尚(){
while(1){
  //要有桶才能喝水
  //缸中要有水才能喝水
  //要互斥访问缸
  P(full);
  P(tong);//申请桶
  P(gang);
  拿着桶到缸里面取水
  V(gang);
  V(empty);
	饮水;
  V(tong);
  

}
  
}






标准答案
image.png

2.理发师问题——进程间关系为“服务-被服务”

image.png
可以看成同步问题 服务业
你 理发师
生产者 消费者
image.png
image.png
image.png
image.png
通常要用一个变量来记录等待的顾客有多少个


理发师(){

是否有顾客?
  无——>睡觉
  有——>提供服务
 while(1){
  	if(num>0){
    	//说明有顾客
       提供服务
    }else{
  
  		睡觉;
		}

  }
}


顾客(){

	理发师在忙?
  忙——>检查座位够不够,如果座位够,就坐下来等。如果不够,就离开
  不忙——>叫醒理发师
 

}






3.读者写者问题——同类进程不互斥、异类进程互斥

:::info

  • 读者写者问题主要是解决互斥,他的访问关系分为两种题型,一种是可以同时访问(读和读),另外一种是必须互斥访问(写和读,写和写),因为多种关系,所以引入计数器count
  • 互斥:mutex。写过程是不容其他过程的,所以直接P(mutex),而读过程P(mutex)受计数器count影响。第一个读进程才需要P操作,最好一个读进程退出时负责V操作
  • rw:读写之间的互斥,对文件互斥访问。
    :::

例题1:车辆在桥上行驶问题

image.png

Semaphore bridge=1;
从南向北的车(){
//车要在桥上行驶,要占用桥资源,互斥访问桥
  P(bridge);
 在桥上行驶;
 车从桥上驶离;
 V(bridge);

}
从北向南的车(){
//车要在桥上行驶,要占用桥资源,互斥访问桥
  P(bridge);
 在桥上行驶;
 车从桥上驶离;
 V(bridge);

}


读者写者问题
允许一个方向有多辆车行驶
不允许两车交会
要占用桥资源
同类进程共享资源,不同类进程互斥使用资源

Semaphore bridge=1;//互斥访问桥
int sn=0;//从南向北的车的数量
int ns=0;//从北向南的车的数量
Semaphore mutex_sn=1;//互斥访问sn
Semaphore mutex_ns=1;;/互斥访问ns
从南向北的车(){
  
//使用sn要申请
  P(mutex_sn);
  //如果车的数量为0,则要申请桥资源,否则直接在桥上行驶
  if(sn==0){
  	P(bridge);
  }
	//车数量+1
  sn++;
  V(mutex_sn);
  车在桥上行驶;
  //如果最后一辆车行驶过去,则释放桥
  P(mutex_sn);
  sn--;
  if(sn==0){
  	v(bridge);
  }
  V(mutex_sn);
	V(sn);

}


从北向南的车(){

//使用ns要申请
  P(mutex_ns);
  //如果车的数量为0,则要申请桥资源,否则直接在桥上行驶
  if(ns==0){
  	P(bridge);
  }
	//车数量+1
  ns++;
  V(mutex_ns);
  车在桥上行驶;
  //如果最后一辆车行驶过去,则释放桥
  P(mutex_ns);
  ns--;
  if(ns==0){
  	v(bridge);
  }
  V(mutex_ns);
	V(ns);



}


image.png

例题2:猴子穿越峡谷

image.png


同一个方向可以有多只狒狒通过
同一时间不允许两个方向都有狒狒通过
读者写者问题

Semaphore rope=1;//互斥访问绳索
Semaphore mutex_es=1;//用于保护es
Semaphore mutex_se=1;//保护se
int es=0;//从东到西穿越的狒狒个数
int se=0;//从西到东穿越的个数

从东到西的狒狒(){

  //如果是第一只要穿越,要占用绳索
  P(mutex_es);
  if(es==0){
  	P(rope);
  }
  es++;
  V(mutex_es);
	攀住绳子穿越峡谷;
  //如果最后一只穿越过去,则释放绳索
	P(mutex_es);
  es--;
  if(es==0){
  	V(rope);
  }
  V(mutex_es);
  V(rope);
   



}

从西到东的狒狒(){

  //如果是第一只要穿越,要占用绳索
  P(mutex_se);
  if(se==0){
  	P(rope);
  }
  se++;
  V(mutex_se);
	攀住绳子穿越峡谷;
  //如果最后一只穿越过去,则释放绳索
	P(mutex_se);
  se--;
  if(se==0){
  	V(rope);
  }
  V(mutex_se);
  V(rope);
   


}



例题3:录像厅问题

image.png
观看不同录像片的观众互斥使用录像厅
image.png

4.哲学家进餐问题——只有一类进程,每个进程需要同时拥有多种资源才能运行

思路

image.png
以后,如果在考试中,遇到一个进程需要一口气取走所有资源的题目,可以利用互斥变量mutex,
P(mutex);
申请各种资源;
V(mutex);
第三个思路最通用:
①将每种资源通过int类型的变量定义(使用变量将资源数量具象化)
②在进程开始运行时,先使用P(MUTEX)操作实现对各种资源进行互斥的访问,目的是逐一判断当前进程所需的各种资源是否满足运行的需要
③如果资源都满足,则拿走这些资源(一口气拿走),然后V(MUTEX),再执行动作
循环条件下:如果只要有一个资源不满足,则V(MUTEX),然后结束此次循环,进行下一次循环(continue);
只进行一次:如果只要有一个资源不满足,则使用goto语句返回函数的第一句重新进行判断(手动实现循环)
其中②实现同一时间只可能有一个进程在进行判断/拿走临界资源
最后完成动作归还资源时也要进行上锁,保证归还动作一气呵成的完成

第一步:定义锁  semaphore mutex = 1;    //互斥信号量,同时仅允许一个进程判断并拿走临界资源
第二步:定义资源数目 int a = n;    //用int类型表示各种资源
									 int b = m;
代码模板
 
Philosopher () {    //进行多次,while
 第三步:一口气拿走所有资源   while (1) {
        P(mutex);
        if (所有资源都足够) {    //资源够用,将所需资源分配给该进程,并解锁
           所有资源的int值减少;
            取xxx资源//一口气拿完所有资源
            V(mutex);//拿完资源以后解锁
            break;//跳出循环
        }  
      	如果资源不够,就解锁,再循环尝试一次
      	V(mutex);
    }//while循环结束
第四步:做进程该做的事,比如吃饭 
第五步:一口气归还所有资源
        P(mutex);    //完成动作后,归还资源
        归还所有资源,所有资源的int值增加
        V(mutex);
    }

 

干饭问题

俗话说,“干饭人,干饭魂,干饭人吃饭得用盆"。一荤、一素、一汤、一米饭,是每个干饭人的标配。饭点到了,很多干饭人奔向食堂。每个干饭人进入食堂后,需要做这些事:拿一个盆打荤菜,再拿一个盆打素菜,再拿一个盆打汤,再拿一个盆打饭,然后找一个座位坐下干饭,干完饭把盆还给食堂,然后跑路。现在,食堂里共有N个盆,M个座位。请使用P、V操作描述上述过程的互斥与同步,并说明所用信号量及初值的含义。

下面我们模仿哲学家进餐问题的第二种解决思路。在哲学家问题中,共有5个哲学家,如果我们限制“最多允许4个哲学家同时进餐",那么至少会有一个哲学家可以同时获得左右两只筷子,并顺利进餐,从而预防了死锁。

    同样的思路可以迁移到干饭人问题中。每个干饭人需要同时持有4个盆才能干饭,那么最糟糕的情况是每个干饭人都持有3个盆,同时在等待第四个盆。此时,但凡再多一个盆,就至少能有一个干饭人可以顺利干饭,就不会死锁。因此我们可以限制同时抢盆的人数为x,那么只要满足3x+1≤N,则一定不会发生死锁,可得x≤(N-1)/3。参考代码如下:


上面这种做法,限制了人数上限,且先拿盆,再占座,一定不会发生死锁。当然,如果先占座、后拿盆,也不会死锁。事实上,如果座位的数量满足seat ≤ (N-1)/3,那么甚至可以不设置专门的信号量x,完全可以先占座,后拿盆,也一定不会死锁。因为座位的数量就可以限制同时抢盆的人数。

    下面我们再模仿哲学家问题的第三种解决思路一—仅当一个哲学家左右两边的筷子都可用时才允许哲学家拿筷子。其实就是破坏了请求和保持条件,采用"静态分配"的思想,让进程一口气获得所有资源,再开始运行。代码如下:

2019年真题

image.png
image.png


Semaphore mutex=1;//互斥
int wan=m;//碗
int chop[n]={1,1,1...,1};//筷子
哲学家(){
  //一个哲学家把所有资源都获得以后,其他哲学家才能取资源
  while(1){
		P(mutex); 
		//要先申请碗,先判断资源足不足够,如果资源不够就要释放锁
  	if(wan<=0) {
      V(mutex);
      continue;
    }
    //判断筷子资源够不够
  	if(!(chop[i]==1&&chop[i+1]==1)){
    	V(mutex);
      continue;
    }
    //说明资源足够
    chop[i]=0;//取走左边筷子
    chop[i+1]=0;//取走右边筷子
    wan--;
    V(mutex);
    进餐
    还筷子
    chop[i]=1;
    chop[i+1]=1;
  	思考;

  }
 
   

}


5.单纯的同步问题——前驱后继图

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

不断前进的皮卡丘

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

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

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

打赏作者

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

抵扣说明:

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

余额充值