英文:
Can we use one variable - turn only in Peterson's solution?
问题
这是我们讲座中介绍的Peterson算法:
//P0:
do {
flag[0] = TRUE; turn = 1;
while(flag[1] && turn == 1);
// 临界区
flag[0] = FALSE;
// 剩余部分
} while(1)
//P1:
do {
flag[1] = TRUE; turn = 0;
while(flag[0] && turn == 0);
// 临界区
flag[1] = FALSE;
// 剩余部分
} while(1)
turn
变量本身不就已经满足了临界区的要求吗?像这里的flag[]
似乎是不必要的。
英文:
Here is the Peterson's solution presented in our lecture:
//P0:
do {
flag[0] = TRUE; turn = 1;
while(flag[1] && turn == 1);
//Critical section
flag[0] = FALSE;
//remainder section
} while(1)
//P1:
do {
flag[1] = TRUE; turn = 0;
while(flag[0] && turn == 0);
//critical section
flag[1] = FALSE;
//remainder section
} while(1)
Doesn't the turn variable itself already guaranteed the requirements for critical section? Like the flag[]
here seems unnecessary.
答案1
得分: 5
flag
数组指定了每个进程当前是否对临界区感兴趣(即它当前是否希望进入临界区或者已经在其中)。这些信息是必要的,原因如下:
假设进程#0希望进入临界区,它必须知道进程#1当前是否也对临界区感兴趣,因为进程#0必须根据进程#1是否对临界区感兴趣而采取不同的行动。
如果进程#1不对临界区感兴趣,那么进程#0不应该等待进程#1将turn
设置为0
,因为它可能无限期地等待这种情况发生,这将违反“进展”和“有界等待”的要求。在这种情况下,进程#0应该直接进入临界区。
如果进程#1对临界区感兴趣,那么进程#0应该等待进程#1要么
- 离开临界区(进程#1通过将
flag[1]
设置为false
来指示),或者 - 让给进程#0(进程#1通过将
turn
设置为0
来指示)。
否则,将违反“互斥”要求。
英文:
The flag
array specifies for each process whether it is currently interested in the critical section (i.e. whether it currently desires to enter it or is already inside it). This information is necessary for the following reason:
Assuming that process #0 desires to enter the critical section, it must know whether process #1 is currently also interested in the critical section, because process #0 must act differently depending on whether process #1 is also interested in the critical section.
If process #1 is not interested in the critical section, then process #0 should not wait for process #1 to set turn
to 0
, because it may wait indefinitely for this to happen, which would violate the "progress" and "bounded waiting" requirement. Instead, process #0 should simply enter the critical section in that case.
If process #1 is interested in the critical section, then process #0 should wait for process #1 to either
- leave the critical section (which process #1 indicates by setting
flag[1]
tofalse
), or - yield to process #0 (which process #1 indicates by setting
turn
to0
).
Otherwise, the "mutual exclusion" requirement would be violated.
答案2
得分: 3
接受的答案对Peterson算法提供了很好的解释,并解释了为什么需要turn
和flag
的理由。这种分析也可以通过更加“实际操作”的方式来进行,即尝试在保持关键属性(互斥、进展和有界等待)的同时,从算法中消除flag
变量。
但是,如果删除所有带有flag
的语句,则显然会出现问题,例如如果P0设置turn=1
并在turn==1
的情况下开始等待,它将无限期地等待,除非P1设置turn=0
。因此,“进展”属性会丧失。
我们可以尝试修复这个问题,方法是将turn
设置为“我的”值(P0为“0”,P1为“1”),并等待直到turn
的值为“我的”值。这显然需要在退出临界区时将turn
设置为“其他”进程的值(否则,“其他”进程可能会在它的while
循环中无限期地等待,失去“有界等待”属性):
//P0:
do {
turn = 0;
while(turn != 0);
// 临界区
turn = 1; // 完成临界区,让P1执行
// 剩余部分
} while(1)
//P1:
do {
turn = 1;
while(turn != 1);
// 临界区
turn = 0; // 完成临界区,让P0执行
// 剩余部分
} while(1)
然而,这并不能确保“互斥”属性:例如,在P0在其临界区时,P1可以愉快地将turn=1
,然后继续执行其临界区。
得到的教训是:在处理并发性时要非常小心。到处都有错误的机会,而在编码时很难看到它们,甚至在事后进行故障排除时也很困难。在可能的情况下,尽量采用像Peterson算法这样多年来经过彻底分析和优化的成熟方法。
英文:
While the accepted answer provides a good explanation of Peterson's algorithm and justification of why both turn
and flag
are needed, this analysis can also be carried out in a more "hands on" fashion by trying to eliminate the flag
variable from the algorithm while maintaining the key properties mutual exclusion, progress, and bounded waiting.
Just deleting all statements with flag
leaves the obvious problem that if e.g. P0 sets turn=1
and starts waiting while turn==1
then it will wait indefinitely unless P1 sets turn=0
. Hence the "progress" property is lost.
Let us try to repair it by instead setting turn
to "my" value ("0" for P0 and "1" for P1) and wait until turn
has "my" value. This clearly requires setting the turn
to the value of the "other" process when exiting the critical section (otherwise, the "other" process could be waiting indefinitely in its while
-loop, losing the "bounded waiting" property):
//P0:
do {
turn = 0;
while(turn != 0);
//Critical section
turn = 1; // Done with critical section, let P1 have a go
//remainder section
} while(1)
//P1:
do {
turn = 1;
while(turn != 1);
//critical section
turn = 0; // Done with critical section, let P0 have a go
//remainder section
} while(1)
However, this does not ensure the "mutual exclusion" property: while e.g. P0 is in its critical section, P1 can happily set turn=1
and proceed to its critical section.
The lesson learned is: be very careful when it comes to concurrency. There are opportunities for mistakes everywhere and they can be difficult to see while coding and even more difficult to troubleshoot afterwards. Whenever possible, embrace established methods like Peterson's Algorithm which have been analyzed and optimized thoroughly over the years.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论