PV操作
考研408常见类型整合
基本流程
- 有几类进程?——每一类对应一个函数
- 在函数内部,用中文描述做了什么事情。——只做一次(不加while),不断重复(加while)
- 每个动作之前,想想是否要P什么?——若写P则必V,想想什么时候V
- 所有pv写完之后在去定义信号量
- 检查多个p操作连续出现的情况,是否会产生死锁
tips:
- 某信号量连续出现不会死锁(即中间无别的P)
- 连续多个P导致的死锁,可尝试调整顺序
- 注意隐含互斥条件(临界区需要互斥访问)
常见考点类型
实现前驱关系,生产者消费者,哲学家,读写者(读优先/写优先/公平),理发师,吸烟者等
模板与案例
实现前驱关系
// 主要就是同步问题
semaphore a1=a2=b1=b2=c=e=d=0;
S1(){
...;//工作
V(a1);V(a2);
}
S2(){
P(a1);
...;//工作
V(b1);V(b2);
}
S3(){
P(a2);
...;//工作
V(c);
}
S4(){
P(b1);
...;//工作
V(d);
}
S5(){
P(b2);
...;//工作
V(e);
}
S6(){
P(c);
P(d);
p(e);
...;//工作
}
究极无敌tips
送分题没啥好说的
生产者消费者
// 无标准模板,根据流程分析,直接手撕~
// 两个生产车间动作不一致,外加装配车间->3个函数
// 车间都是重复操作->while
// 货架就是临界区,注意互斥访问
// 写一个P立即思考它的V在哪
p1(){// 生产A
while(1){
生产A;
P(empty1);
P(mutex1);
放F1;
V(mutex1);
V(full1);
}
}
p2(){// 生产B
while(1){
生产B;
P(empty2);
P(mutex2);
放F2;
V(mutex2);
V(full2);
}
}
p3(){// 装配
while(1){
P(full1);
P(mutex1);
拿A;
V(mutex1);
V(empty1);
P(full2);
P(mutex2);
拿B;
V(mutex2);
V(empty2);
组装;
}
}
//最后定义信号量,但在试卷上要在函数之前定义
semaphore mutex1,mutex2=1; //实现对两个货架的互斥访问
semaphore full1=full2=0; //货架上的产品数
semaphore empty1=empty2=10; //货架上的空位
究极无敌tips
老老实实分析就完事了~
有的情况是一次性取很多个,务必保证取十个的动作一气呵成完成!
哲学家
//只有一类进程,但要占用多种资源
//1. 定义大锁->semaphore Lock=1;
//2. 资源数->int a=0;b=8;c=5;
process(){
while(1){
P(Lock);
if(资源都够){
所有资源都减少;
取xx资源;
V(Lock);
break;
}
V(Lock);
}
做事(比如吃饭);
P(Lock);
归还所有资源,int值增加;
V(Lock);
}
究极无敌tips
无脑套模板就完事了
Semaphore mutex=1;//对资源的互斥访问
int pot=N;
int sear=M;
Eat(){
进食堂;
while(1){
p(mutex);
if(pot>=4 && seat>=1){//资源足够
pot=pot-4;
seat=seat-1;
v(mutex);//拿完资源解锁
break;
}
v(mutex);//资源不够解锁
}
打菜吃饭;
P(mutex);//一口气归还所有资源
pot=pot+4;
seat=seat+4;
V(mutex);
离开;
}
读写者
//读优先
int count=0;//读者数
semaphore mutex=1;//互斥访问count
semaphore Lock=1;//互斥使用资源
writer(){
while(1){
P(Lock);//互斥使用资源
写;
V(Lock);
}
}
reader(){
while(1){
P(mutex);
if(cout==0){
P(Lock);//当第一个读者来时,上锁,只允许读者们使用
}
count++;
V(mutex);
读;
P(mutex);
count--;//读完了,读者-1
if(count==0){
V(Lock);//最后一个读者完事后,把锁打开,这样写者可以使用
}
V(mutex);
}
}
//写优先
int readcount=0;//读者数
int writercount=0;//写者数
semaphore readmutex=1;//互斥访问readcount
semaphore writermutex=1;//互斥访问writercount
semaphore read=1;//给读者上锁
semaphore writer=1;//给写者上锁
writer(){
while(1){
P(writermutex);
writecount++;
if(writercount==1){
P(read);//第一个读者对读者上锁,实现写优先
}
V(writermutex);
P(writer);//每个读者写之前都要P(witer),保证写者之间的互斥,也能保证若有读者,写者会阻塞等待
写;
V(writer);
P(writermutex);
writecount--;
if(writercount==0){
V(read);//最后一个写者写完后,对读者解锁
}
V(writermutex);
}
}
reader(){
while(1){
P(read);//每个读者到达时先对read上锁
P(readmutex);
readcount++;
if(readcount==1){
P(writer);//第一个读者对write上锁,不让写者写
}
V(readmutex);
V(read);//每个读者开始读之前先对read解锁
读;
P(readmutex);
readcount--;
if(readcount==0){
V(write);//最后一个读完的对write解锁,允许写者写
}
V(readmutex);
}
}
//读写公平
int count=0;//读者数
semaphore mutex=1;//互斥访问count
semaphore Lock=1;//互斥使用资源
semaphore queue=1;//用于实现公平,可将其理解为一个队列,当资源不可访问时,都需要公平排队
writer(){
while(1){
P(queue);
P(Lock);//互斥使用资源
写;
V(Lock);
V(queue);
}
}
reader(){
while(1){
P(queue);
P(mutex);
if(cout==0){
P(Lock);//当第一个读者来时,上锁,只允许读者们使用
}
count++;
V(mutex);
V(queue);
读;
P(mutex);
count--;//读完了,读者-1
if(count==0){
V(Lock);//最后一个读者完事后,把锁打开,这样写者可以使用
}
V(mutex);
}
}
理发师
// 核心思路就是将其改造成生产者消费者问题
// 第一类:服务者可以睡觉休息,先取号再等号,无等位上限
int waiting=0;//等待服务的顾客数
semaphore mutex=1,customer=0,server=0;//互斥访问waiting|顾客资源|服务员资源
server_i(){//n个服务员进程
while(1){
P(customer);//消耗一个顾客资源,若无顾客则休息
P(mutex);
waiting--;//等待人数-1(相当于叫号)
V(server);//释放一个服务员资源
V(mutex);
提供服务;
}
}
customer(){
P(mutex);
waiting++;//等候顾客数+1(相当于取号)
V(customer);//释放一个顾客资源
V(mutex);
P(server);//消耗一个服务资源
被服务;
}
// 第二类:服务者不睡觉(忙等),先取号再等号,无等位上限
int waiting=0;//等待服务的顾客数
semaphore mutex=1,server=0;//互斥访问waiting|服务员资源
//这里不用定义顾客资源,因为参考第一类,若写了会造成阻塞(睡觉),不符合题意了
server_i(){
while(1){
P(mutex);
if(waiting>0){
waiting--;//等待人数-1(相当于叫号)
V(server);//释放一个服务员资源
V(mutex);
}else{
V(mutex);
continue;// 忙等
}
提供服务;
}
}
customer(){
P(mutex);
waiting++;
V(mutex);
P(server);//消耗一个服务资源
被服务;
}
// 第三类:服务者可以睡觉休息,有座位限制,若无则离开
int waiting=0;//等待服务的顾客数
int chair=m;//位子数
semaphore mutex=1,customer=0,server=0;//互斥访问waiting|顾客资源|服务员资源
server_i(){
while(1){
P(customer);//消耗一个顾客资源
P(mutex);
waiting--;//等待人数-1(相当于叫号)
V(server);//释放一个服务员资源
V(mutex);
提供服务;
}
}
customer(){
P(mutex);
if(waiting<chair){
waiting++;
V(customer);//释放一个顾客资源
V(mutex);
P(server);//消耗一个服务资源
被服务;
}else{
离开;
V(mutex);
}
}
究极无敌tips
按照情况进行分类,此类具有一定区分度,但分出类别再使用模板,直接手撕~
// 分析类型(服务者可以睡觉,有座位限制)
int waiting=0;//等待服务顾客数
int char=n;//椅子数
semaphore mutex=1,customer=0,barber=0;
barber(){
while(1){
P(customer);//消耗一个顾客资源,没有顾客就阻塞(睡觉)
P(mutex);
waiting--;
V(barber);//释放一个理发师资源(叫醒)
V(mutex);
剪发;
}
}
customer(){
P(mutex);
if(waiting<chair){
waiting++;
V(customer);
V(mutex);
P(barber);
被服务;
}else{
离开;
V(mutex);
}
}
吸烟者
// 本质上就是生产者消费者问题(一生产多消费)
int num=0;// 用于存储随机数(用于实现轮流抽烟)
semaphore offer1=0;//定义信号量对应烟草和纸组合的资源
semaphore offer2=0;//定义信号量对应烟草和胶水组合的资源
semaphore offer3=0;//定义信号量对应纸和胶水组合的资源
process1(){
while(1){
num++;
num=num%3;
if(num==0){
V(offer1);
}else if(num==1){
V(offer2);
}else{
V(offer3);
}
放对应的两种材料;
P(finish);//没抽完之前出于阻塞状态,抽完后进行下一组分配
}
}
process2(){
while(1){
P(offer1);
拿所需材料抽烟;
V(finish);
}
}
process3(){
while(1){
P(offer2);
拿所需材料抽烟;
V(finish);
}
}
process4(){
while(1){
P(offer3);
拿所需材料抽烟;
V(finish);
}
}
模板后面对应的是例题,但是例题的图片被笔者弄丢了,望见谅,内容有误可指出~