临界区问题:一种新的视角

首先阐明两个概念:

临界资源:必须互斥访问的资源(如公共变量,打印机等)。

临界区:访问临界资源的一段代码。

 

对于一个进程Pi,其典型结构如下:

do

{

    进入临界区

临界区               ; 访问临界资源的代码

退出临界区

剩余区               ; 其余的代码

}while(1);

 

考虑两个进程P0, P1并发执行。如何保证它们互斥访问临界资源呢?

 

前人提出了3种算法。

 

算法1

P0,P1共享一个bool变量turnturn取值为01

turn == i, 则允许Pi在临界区内执行。

 

Pi的结构如下:

do

{

    While (turn != i) ;     // turn != i,循环等待

临界区

turn = j;

剩余区

}while(1);

 

这种算法能够保证互斥性,但它要求P0,P1在临界区中的执行是严格交替的。如果turn=0,且P1要进入临界区,那么尽管P0可能在其剩余区内,P1并不能进入。

 

算法2

引入一个bool数组flag[2];数组元素初始化为false。如果flag[i]=true,表明Pi准备进入临界区。

 

Pi结构如下:

do

{

    flag[i] = true;

    while (flag[j]) ;   // flag[j] == true,则循环等待

临界区

flag[i] = false;

剩余区

}while(1);

 

该算法中,Pi首先置flag[i]为真,表明它准备进入临界区,接着,它检查并验证Pj没有准备进入临界区。但这个算法仍然不行,如果flag[0] = flag[1] = true,则两个进程会无限等待下去(具体分析省略)。

 

算法3

将算法1和算法3结合起来:

bool flag[2];

int turn;

初始时,flag[0] = flag[1] = false;

 

Pi结构如下:

do

{

    flag[i] = true;

    turn = j;

    while(flag[j] && turn == j) ;

临界区

flag[i] = false;

剩余区

}while(1);

 

可以证明这个解答是正确的。

 

下面,我从“礼”的角度来分析这3个算法。

临界区问题即要求进程能够互斥访问临界资源。系统的实现决定了要实现这个目标,必须进程“自觉”遵守相关规定(当然,可以依靠操作系统的某些机制)。

“自觉”就要讲“礼”。

算法1只是使用一个turn变量,而算法2使用了一个数组。这个数组分别记录了两个进程各自的“意愿”。每个进程在试图进入临界区的时候,都先“体察”一下对方的“意愿”,这就懂得了“礼让”。

但算法2仍然不能解决问题。为什么呢?从中国传统的“礼”来说,中国人喜欢“假客气”。例如别人对你说:“吃个苹果吧。”你虽然想吃,但你通常会说:“不用了。”只有别人把苹果“硬塞”给你,你就手下了(因为你确实想吃)。除非你真的是讨厌苹果,否则你不会拒绝。

所以,在“体察”对方“意愿”的时候,可能会获得不正确的结果。

算法3的高明之处在于,先将turn设置成对方。然后在看对方的“意愿”。这就像:我先把苹果“硬塞”给对方,然后看他是否接受。如果他心里想要,就会接受。这时候你就知道了他真实的意愿。如果他确实不想吃,那么他就会推脱。

在这种方式下,你“体察”到的“意愿”是真实的,而不是经过掩饰的虚假。

 

从“礼”的角度来分析,至少有3点好处:

1.    理清算法的流程。

2.    延伸算法的思想内涵。

3.    对设计新的算法提供了思路(例如,经过研究现实生活中“礼”的形式,提出更优化的算法)

 

 

By 夏至逃亡 @TITAN Studio

posted on 2008-08-24 11:28  荒芜森林  阅读(1793)  评论(3编辑  收藏  举报

导航