我们在Peterson的解决方案中只能使用一个变量吗?

huangapple go评论62阅读模式
英文:

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] to false), or
  • yield to process #0 (which process #1 indicates by setting turn to 0).

Otherwise, the "mutual exclusion" requirement would be violated.

答案2

得分: 3

接受的答案对Peterson算法提供了很好的解释,并解释了为什么需要turnflag的理由。这种分析也可以通过更加“实际操作”的方式来进行,即尝试在保持关键属性(互斥、进展和有界等待)的同时,从算法中消除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.

huangapple
  • 本文由 发表于 2023年3月8日 17:15:44
  • 转载请务必保留本文链接:https://go.coder-hub.com/75671222.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定