本篇文章,讲解了PV操作的作用,举了两个情景模拟pv操作的应用原理,同时讲解了PV操作涉及到的基本概念,并且在文章的最后用伪代码写了pv操作过程。
要理解PV操作,请先看两个场景:
场景1:飞机场售票窗口进程
Process Pi(i=1,2,3,…,n)
Begin
查找当天机票剩余量A
If A>1 then
A=A-1;
售出一张票
Else 输出票以售完
End
若同时有5个窗口执行该程序,如果5个程序同时并发执行,同时取到相同的票数(实际情况中:用锁就能解决该问题,不会取到相同的票数,此处只是为说明pv操作),则售了5张票,而实际票数只减了1张。
场景2:进程A向缓存中写数据(缓存只存一条数据),进程B从缓存存数据。如果在B未取走数据前就写入数据,数据会被覆盖;若A还没写入新数据前B写入一次数据,则会读取到旧数据,怎么保证两个进程能够同步执行?
情景一为互斥问题,当一个进程读取并修改数据的时候,另一个进程不能读取数据;情景二为同步问题,只有在进程A写入数据后,B才能读数据,否则可能读到空数据或者重复数据;B读取完,A才能写入数据,否则可能数据还没有读的时候就被覆盖掉。
使用PV操作可以解决上面的两个问题,pv操作通过观察信号量,从而控制多个进程间同步或互斥使用资源。
理解PV操作需要理解几个重要的概念:临界区,P操作,V操作,同步和互斥。
进程的互斥:若干个进程都要访问某一共享资源时,任何时刻最多只允许一个进程使用该资源,其他要使用该资源的进程必须等待,知道该资源的占用者释放进程。
进程的同步:并发进程间存在一种制约关系,一个进程的执行依赖另一个进程的消息,当一个进程没有得到另一个进程的消息时等待,知道消息到达才被唤醒。
临界区:当N个进程都要访问同一个变量时,我们把与共享变量有关的程序段成为临界区。所有涉及该变量的临界区成为相关临界区。
信号量:表示临界区的进程是否可以往下执行,用S signal 代表信号量 ,s>0,代表当前变量或资源可以使用,该进程可以执行;S=0,表示当前无资源,当前进程不可往下执行;s<0表示当前有等待使用资源的进程,并且该负数的绝对值代表等待资源的进程数
p操作:判断调用P操作的进程是否可以获得资源并往下执行,如果信号量减一后大于0,将会往下执行,否则继承进入等待队列;
用代码表示:
Procedure P(Var S:Semaphore);
Begin
s=s-1;
If s<0 then W(s);
End;{P}
V操作:进程释放一个变量或资源,信号量加一,若等待队列有进程,则释放一个等待进程(该进程获取资源并执行)
Procedure V(Var S:Semaphore);
Begin
s=s+1
if S<=0 then r(s)
end
那么了解了PV操作和相关的概念后,我们可以试着用PV操作解决文章开始的两个场景了:
解决场景一:将PV操作将如到场景一的伪代码中
Begin
S:semaphore //定义信号量
S=1;
Process Pi(i=1,2,3,…,n)
Begin
查找当天机票剩余量A
P(S); //进行P操作,当前的信号量减1大于等于0,则继续进行,否则等待资源
If A>1 then
A=A-1;
V(S) //将当前变量或资源释放,将信号量加一,结果如果小于等于0,则唤醒一个进程
售出一张票
Else 输出票以售完
End
End
那么此时,由于将信号量设置为1,只能有一个进程访问并修改机票数量,因此不会出现问题。
有此可见,当信号量为正时,信号量的数值代表有几个可用资源,为0时,表示当前没有可用资源,为负值时说明当前有进程在等待资源。
解决场景二:
Begin
Buffer:integer
SA,SB:semaphore;
SA=1,SB=0;//定义A进程的信号量和B进程的信号量
Process A
Begin
L1: 向缓存中写入一个数据Data;
P(SA); //进行P操作,判断当前进程A是否可以向缓存写入数据
Buffer=Data;
V(SB); //A进程写入说后,将B进程的信号量加一,告诉B进程可以进行读数据了
Goto L1;
End ;
Process B
Begin
L2: P(SB); //进行P操作,判断当前进程B是否可以从缓存读数据
从缓存取出一个数据Data;
V(SA); //读完数据后,将A进程的信号量加一,告诉A进程可以写入数据了
用取出的数据进行计算;
Goto L2;
End
End
PV操作解决了多个进程间共享数据和资源时,误操作的问题,保证了安全的共享资源。