<b>一、PV</b><b>原语的含义</b>
<b></b>
P操作和V操作是不可终端的程序段,成为原语,PV原语及信号量的概念都是由荷兰科学家E.W.Dijkstra提出的。信号量sem是一个整数。Sem大于等于零时代表可供并发进程使用的资源实体数,但sem小于零时则表示正在等待使用临街区的进程数。
原语操作举例如下,P操作与V操作不允许中断:
procedure p(var s:samephore);
{
s.value=s.value-1;
if (s.value
}
procedure v(var s:samephore);
s.value=s.value+1;
<b>二、P</b><b>原语操作的动作是:</b>
(p可理解为pass,获得一个资源)
<b>(1) </b><b>sem</b><b>减 1 ;</b>
<b>(2) </b><b>若sem减 1 后仍大于等于零,则请求的进程继续执行;</b>
<b>(3) </b><b>若sem减 1 后小于零,则进程被阻塞后进入与该信号相对应的队列中,然后转进程调度。</b>
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiIn5GcuQTM0M2M5kTM5U2M0QzNmNGMmljNxYjYycTNmNWYhdTMfdWbp9CXt92Yu4GZjlGbh5SZslmZxl3Lc9CX6MHc0RHaiojIsJye.png)
图 P操作流程
<b>V</b><b>原语操作的动作是:</b>
(V可理解为Release,释放一个资源)
<b>(1) </b><b>sem</b><b>加 1 ;</b>
<b>(2) </b><b>若相加结果大于零,则请求的进程继续执行;</b>
<b>(3) </b><b>若相加结果小于等于零,则从该信号的等待队列中唤醒一个等待进程,然后再返回原进程继续执行或转进程调度。</b>
PV操作对于每一个进程来说,都只能进行一次,而且必须成对使用,在PV原语执行期间不允许有中断的发生。
图 V操作流程
进程P1 进程P2 …… 进程Pn
…… …… ……
P(S); P(S); P(S);
临界区; 临界区; 临界区;
V(S); V(S); V(S);
…… …… …… ……
其中信号量S用于互斥,初值为1。
<b>使用PV操作实现进程互斥时应该注意的是:</b>
⑴每个程序中用户实现互斥的P、V操作必须成对出现,先做P操作,进临界区,后做V操作,出临界区。若有多个分支,要认真检查其成对性。
⑵P、V操作应分别紧靠临界区的头尾部,临界区的代码应尽可能短,不能有死循环。
⑶互斥信号量的初值一般为1。
现在,简单的说一下“信号灯”机制,打一个跟厕所有关的比喻,(不是很雅^_^),在火车上,当A上厕所的时候,他会将厕所的门锁上,对外标识一个“有人”的表示,当他解决完以后,他会打开门,“有人”的标识就会变成“无人”的标识,这样B就可以进去了,而如果A不把这个标识改变,B就算憋死也进不去的。(当然暴力方式排除在外)。C# 提供的 <b>AutoResetEvent</b>就是这样一种“信号灯”,定义这个类的变量,并传递到子线程,在子线程将结束时,将该“信号灯”设置为 true(“无人”)状态,这样主线程就可以进入了(开始执行)。
<b>三个问题:</b>
三,Sem小于0应该是说没有临界资源可供使用,为什么还要唤醒进程?
<b>析疑:</b>
一,P操作对sem减1的。P、V原语必须成对使用!从而不会造成死循环。
二,Sem大于0的确表示有临界资源可供使用,而且这个时候没有进程被阻塞在这个资源上,也就是说没有进程因为得不到这类资源而阻塞,所以没有被阻塞的进程,自然不需要唤醒。
三,V原语操作的本质在于:一个进程使用完临界资源后,释放临界资源,使Sem加1,以通知其它的进程,这个时候如果Sem队列里唤醒一个进程来“转手”该类资源。比如,有两个某类资源,四个进程A、B、C、D要用该类资源,最开始Sem=2,当A进入,Sem=1,当B进入Sem=0,表明该类资源刚好用完, 当C进入时Sem=-1,表明有一个进程被阻塞了,D进入,Sem=-2。当A用完该类资源时,进行V操作,Sem=-1,释放该类资源,而这时Sem
为了进一步加深理解,再引入<b>二个问题:</b>
五,Sem的绝对值表示等待的进程数,同时又表示临界资源,这到底是怎么回事?