彼得森问题
Peterson的解决方案为解决临界区问题提供了很好的算法描述,并说明了设计软件的一些复杂性,这些软件可以满足互斥,进步和有界等待的要求。
do { flag[i] = true; turn = j; while (flag[j] && turn == j); /* critical section */ flag[i] = false; /* remainder section */ } while (true);
Peterson解决方案中流程Pi的结构。此解决方案仅限于两个过程,这些过程在其关键部分和其余部分之间交替执行。进程编号为P0和P1。为了方便起见,我们使用Pj表示存在Pi时的其他过程。也就是说,j等于1-I,Peterson的解决方案要求两个进程共享两个数据项-
int turn; boolean flag[2];
可变匝数表示要进入其关键区域的匝数。也就是说,如果转弯==i,则允许进程Pi在其关键部分执行。如果某个进程准备进入其关键部分,则使用标志数组进行指示。例如,如果flag[i]为true,则此值指示Pi准备进入其临界区。完整解释了这些数据结构后,我们现在就可以描述上述算法了。为了进入关键部分,过程Pi首先将标志[i]设置为真,然后将turn设置为值j,从而断言,如果其他过程希望进入关键部分,则可以这样做。如果两个进程都尝试同时输入,则将在大致相同的时间将转向设置为i和j。这些分配中只有一种最终会发生。另一个将发生,但将立即被覆盖。匝数的最终值确定两个过程中的哪一个首先进入关键部分。现在,我们证明该解决方案是正确的。我们需要证明-
互斥被保留。
满足进度要求。
已满足有界等待条件。
为了证明1,我们注意到只有在flag[j]==false或turn==i时,每个Pi才进入其临界区。另请注意,如果两个进程可以同时在其关键部分中执行,则flag[0]==flag[1]==true。这两个观察结果表明P0和P1不可能在大约同一时间成功执行其while语句,因为turn的值可以是0或1,但不能两者都为。因此,其中一个过程(例如Pj)必须成功执行了while语句,而Pi必须执行至少一个其他语句(“turn==j”)。但是,此时,flag[j]==true并转为==j,只要Pj在其临界区,这种情况就会持续。结果,保留了互斥。
为了证明属性2和3,我们注意到,如果进程被条件标记[j]==true和turn==j困在while循环中,则可以防止进程Pi仅进入临界区;此循环是唯一可能的循环。flag[j]==false,如果Pj尚未准备好进入临界区,则Pi可以进入其临界区。如果Pj已置位且flag[j]=true,并且也在其while语句中执行,则将turn==i或turn==j。如果转弯==i,则Pi将进入临界区。如果转弯==j,Pj将进入临界区。尽管一旦Pj退出其临界区,它将p[j]重置为false,从而允许Pi进入其临界区。如果Pj将flag[j]重置为true,则Pj还必须将turn设置为i。因此,由于Pi在执行while语句时不会更改变量turn的值,
坏处
Peterson的解决方案适用于两个过程,但是此解决方案是用户模式中关键部分的最佳方案。
该解决方案也是一个繁忙的等待解决方案,因此浪费了CPU时间。这样就可能出现“自旋锁定”问题。这个问题可能出现在任何繁忙的等待解决方案中。