为了提高在实际问题中分析和思考多个线程之间同步互斥问题的能力,接下来将讲解PV操作,这也是中的重点和难点。本文将会先简要介绍下PV操作的来源和基本使用方法,然后再通过两道经典的计算机考研真题――放水果和安全岛来示范如何运用PV操作。
先讲讲PV操作的起源和用法。
1962年,荷兰学者Dijksrta在参与X8计算机的开发中设计并实现了具有多道程序运行能力的操作系统――THE Multiprogramming System。为了解决这个操作系统中进程(线程)的同步与互斥问题,他巧妙地利用火车运行控制系统中的“信号灯”(semaphore,或叫“信号量”)概念加以解决。信号量的值大于0时,表示当前可用资源的数量;当它的值小于0时,其绝对值表示等待使用该资源的进程个数。注意,这个信号量的值仅能由PV操作来改变。
PV操作由P操作原语和V操作原语组成(原语也叫原子操作Atomic Operation,是不可中断的过程),对信号量(注意不要和Windows中的信号量机制相混淆)进行操作,具体定义如下:
P(S):
①将信号量S的值减1,即S=S-1;
②如果S>=0,则该进程继续执行;否则该进程置为等待状态。
V(S):
①将信号量S的值加1,即S=S+1;
②该进程继续执行;如果该信号的等待队列中有等待进程就唤醒一等待进程。
用PV操作实现多线程的同步与互斥是非常简单的,只要考虑逻辑处理上合理严密而不用考虑具体技术细节,因此与写伪代码较为相似。比如有多个进程P1、P2、 ……PN。它们要互斥的访问一个资源。用PV操作来实现就非常方便直观。下面是PV操作代码:
设置信号量为S,初值为1。各进程的操作流程如下:
进程进程进程Pn
P(S);(S);(S);
访问资源;访问资源;访问资源;
V(S);(S);(S);
可以看出PV操作会忽略具体的编程细节,让程序员的主要精力放在线程同步互斥的逻辑处理上。因此,通过练习PV操作能快速有效提高程序员对多线程的逻辑思维能力,达到强化“内功”的目的。
接下来就来几道简单的计算机考研真题。
第一题 放水果 南京大学计算机考研真题
桌上有一空盘,允许存放一只水果。爸爸可向盘中放苹果,也可向盘中放桔子,儿子专等吃盘中的桔子,女儿专等吃盘中的苹果。规定当盘空时一次只能放一只水果供吃者取用,请用P、V原语实现爸爸、儿子、女儿三个并发进程的同步。
这个题目涉及的东西非常之多,光人物就有三个再加水果,盘子等等,确实让人感觉好像无从下手。但不管题目如何变,只要牢牢的抓住同步和互斥来分析问题就必定能迎刃而解。
下面先考虑同步情况即所有“等待”情况:
第一.爸爸要等待盘子为空。
第二.儿子要等待盘中水果是桔子。
第三.女儿要等待盘中水果是苹果。
接下来来考虑要互斥处理的资源,看起来盘子好像是要作互斥处理的,但由于题目中的爸爸、儿子、女儿均只有一个,并且他们访问盘子的条件都不一样,所以他们根本不会同时去访问盘子,因此盘子也就不用作互斥处理了。分析至些,这个题目已经没有难度了,下面用PV原语给出答案:
先设置三个信号量,信号量Orange表示盘中有桔子,初值为0。信号量Apple表示盘中有苹果,初值为0。信号量EmptyDish表示盘子为空,初值为1。三个人的操作流程如下所示:
1.爸爸
P(EmptyDish)
if (rand()%2==0)
}
else
{
}
2.儿子
P(Orange)
取桔子
V(EmptyDish)
3.女儿
P(Apple)
取苹果
V(EmptyDish)
第二题 安全岛 南开大学考研真题
在南开大学至天津大学间有一条弯曲的路,每次只允许一辆自行车通过,但中间有小的安全岛M(同时允许两辆车),可供两辆车在已进入两端小车错车,设计并使用P,V实现。
这个问题应该如何考虑了?同样只要牢牢的抓住同步和互斥来分析问题就必定能迎刃而解。
考虑所有“等待”情况:
进入通道之前都需要等待在岛上的位置,有位置才可以进入(同步)
互斥:两个路段都需要互斥
用信号量M来记录安全岛M上空位个数,初值为2。每个进入道路前的人都要先预订安全岛上的空位,订到后再互斥的进入道路。否则就要等待安全岛上有空位。信号量K和L表示道路,初值均为1。然后从N到T的车和从T到N的车的行驶流程如下:
从N到T的车从T到N的车
这种解决方法也是不会造成死锁的。安全岛的解法非常之多,网上还有不少不同的解法,有兴趣的童鞋可以搜索一下。
关于PV操作的总结:
1.在多线程编程中使用PV操作能忽略代码具体的实现细节,这样确保总体思路的正确性。
2.分析PV操作时紧紧抓住同步和互斥;
同步主要分析:谁在等待?等待什么?分析清楚了,同步就出来了;
互斥主要分析:那些资源是共享的,共享资源需进一步思考有没有必要互斥,比如果盘问题中果盘是共享资源,但是果盘操作并不需要互斥;