PV操作可以有效地实现对临界区的管理
设置一个公共信号量s,同时提供两个基于该信号量上的原语操作:P(s)、V(s)
代码实现过程:
P(s) //检测锁状态并上锁
{
s--;
if(s<0)
wait(调用p操作的进程);
//wait为阻塞原语,作用是将括号中的进程置于阻塞态
}
V(s) //解锁
{
s++;
if(s<=0)
release(等待队列)
//release为唤醒原语,唤醒一个等待队列中的进程
}
具体实现方式:
先将信号量s初值置为1(s=1),之后要求每个进入临界区执行的进程执行P操作,离开临界区之前执行V操作,所有试图进入临界区的进程均要求如此。
一个进程执行PV操作的过程:
注:此方式可确保临界区的正确管理
PV操作不仅可以有效管理临界区,还可以管理具体某类资源。设某类资源的数量为n。则设信号量s的初始值为n,执行p操作申请资源,执行V操作释放资源。(执行过程还是如上图所示)
信号量s的取值范围 (s<=n)
同步实现的基础是建立在同步多方的沟通交流机制。因为互斥关系处理中涉及到公共信号量s,实际上信号量s也是一种多方的沟通交流机制,故同步关系的处理也可以接触PV操作进行。
与处理互斥关系不同:
1、同步关系的处理的信号量s的初值依据具体问题的初始状态来定,信号量的数量(定多少个s)是由多少个通信方向的数量来定,信号量的大小(S的大小)根据资源的大小而定。
2、P(s)的意义:用来检测对方发送的信息是否到达;V(s)的意义:用来向对方发送信息。
注:默认在初始情况下,Q进程已经向P进程发送了信息。
在下例中,s1=0表示:在初始情况下,P还没有向Q发送信息;s2=1表示:初始情况下,默认Q已经向P发送了信息。
例:缓冲区看作一个资源。
进程实现代码:
//P进程代码
while(1)
{
...
准备字符
P(s2)//检测Q向P发送的信息是否到达,如果到达则继续执行,反之不执行(挂起)
向缓冲区输入字符
V(s1)//向Q发送信息
...
}
//Q进程代码
while(1)
{
P(s1)//检测P向Q发送的信息是否到达
读出字符并打印字符
V(s2)//向P发送信息
...
}
例子:生产者与消费者问题
问题描述:
有M个生产者不断生产产品(可以看作一个工厂有M个车间),每个生产者每生产出一个产品均推入产品仓库(每一次只可一个产品入库),仓库最多可放K个产品,有N个消费者(可以看作工厂的代理商)不断从仓库取产品消费,要求每个消费者每次只能取一个产品,且任何时候都只能让一个生产者或消费者进入仓库。对该问题进行同步处理使得产品不会滞销也不会脱销,仓库利用率最大。
问题模型:
解决思路:
首先明确该问题中生产者们与消费者们之间的并发关系。并发过程中存在互斥关系(生产者与生产者之间;费者与消费者之间互斥访问仓库)和同步关系(生产者与消费者之间的同步关系)。
设仓库为Buff,大小为K,用数组表示,即Buff[K]
生产者与生产者之间,消费者与消费者之间都为互斥使用仓库,因此设一个公共互斥信号S,仓库即为互斥资源,S初值为1,即:S=1。
生产者与消费者之间是同步关系,设生产者向消费者发送的信息为S1,消费者向生产者发送的信息为S2,初始时可认为仓库为空,相当于消费者向生产者发送了信息告诉生产者可以往仓库放K个产品,反过来生产者告诉消费者能从仓库取0个产品,因此,S1=0,S2=K。
设生产者放产品的位置为in,消费者取产品的位置为out,显然初始时:in=out=0
第i个生产者进程Pi
while(1)
{
生产产品X;
P(S);//检查仓库是否有生产者在访问,互斥关系的P操作
P(S2);//检查消费者的信号是否到达,初始时认为消费者已发送信号给生产者,同步关系的P操作。
Buff[in]=X;//将产品X放入仓库中in的位置
in=(in+1)%K;//下一次放产品的位置(因为到了k后,k+1则需要指向Buff[0],通过除k取余的方式)
V(S);//用完了仓库,则让出仓库,互斥关系的V操作
V(S1);//通知消费者仓库中新放进了一个产品,同步关系的V的操作
}
第j个消费者进程Cj
while(1)
{
P(S);//检查仓库是否消费者在使用,互斥关系的P操作
P(S1);//检测生产者的信号是否到达,初始时认为生产者未发送信号给消费者,同步关系的P操作
Y=Buff[out];//将产品Y从仓库out的位置取出
out=(out+1)%K;//下一次取产品的位置
V(S);//使用完仓库,让出仓库,互斥关系的V操作
V(S2);//通知生产者仓库中被取走了一个产品,同步关系的V操作
}
注:具有同步和互斥关系中,要先处理同步关系,再处理互斥关系。