我们可以使用一个变量 - 在 Peterson 的解决方案中只转弯吗?

Can we use one variable - turn only in Peterson's solution?

提问人:Kenny Ynnek 提问时间:3/8/2023 最后编辑:JabberwockyKenny Ynnek 更新时间:3/8/2023 访问量:213

问:

以下是我们讲座中介绍的 Peterson 解决方案

//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)

转弯变量本身不是已经保证了关键部分的要求吗?就像这里似乎没有必要。flag[]

C++ 算法 并发 操作系统

评论

3赞 Retired Ninja 3/8/2023
你能为那些没有参加你讲座的人解释一下这个代码是关于什么的吗?
0赞 463035818_is_not_an_ai 3/8/2023
即使阅读了维基百科链接,我也不明白算法。也许如果他们假设是原子的,那可能是有道理的。但就 c++ 而言,不是 ,是boolboolatomicstd::atomic<bool>
1赞 463035818_is_not_an_ai 3/8/2023
虽然不清楚你为什么标记 C++。维基百科中的代码只是伪代码,但不正确的 c++
1赞 molbdnilo 3/8/2023
请解释为什么您认为这本身就能保证这些要求。turn
1赞 463035818_is_not_an_ai 3/8/2023
我可以建议删除 c++ 标签并添加与语言无关的标签。代码看起来像 c++,但实际上并不是 c++,这是次优的。它不会使答案无效,因为它不涉及 c++ 细节,而是从与语言无关的 pov 讨论算法

答:

5赞 Andreas Wenzel 3/8/2023 #1

数组为每个进程指定它当前是否对关键部分感兴趣(即它当前是否希望进入它或已经在其中)。出于以下原因,此信息是必需的:flag

假设进程 #0 希望进入关键部分,它必须知道进程 #1 当前是否也对关键部分感兴趣,因为进程 #0 必须根据进程 #1 是否也对关键部分感兴趣而采取不同的行动。

如果进程 #1 对关键部分不感兴趣,则进程 #0 不应等待进程 #1 设置为 ,因为它可能会无限期地等待这种情况发生,这将违反“进度”和“有界等待”要求。相反,在这种情况下,进程 #0 应该只输入关键部分。turn0

如果进程 #1 对关键部分感兴趣,则进程 #0 应等待进程 #1 对

  • 离开关键部分(进程 #1 通过设置为 来指示),或者flag[1]false
  • 屈服于进程 #0(进程 #1 通过设置为 来指示)。turn0

否则,将违反“相互排斥”的要求。

评论

0赞 kiner_shah 3/8/2023
很好,解释很简单:-)
3赞 nielsen 3/8/2023 #2

虽然公认的答案很好地解释了 Peterson 的算法,并证明了为什么两者都需要 AND,但这种分析也可以以更“动手”的方式进行,尝试从算法中消除变量,同时保持关键属性互斥、进度和有界等待turnflagflag

只是删除所有语句,就会留下一个明显的问题,例如,如果 P0 设置并开始等待,那么它将无限期等待,除非 P1 设置 .因此,“progress”属性丢失。flagturn=1turn==1turn=0

让我们尝试通过设置为“my”值(P0 为“0”,P1 为“1”)来修复它,并等待直到具有“my”值。这显然需要在退出关键部分时将 设置为“其他”进程的值(否则,“其他”进程可能会在其 -loop 中无限期等待,从而失去“有界等待”属性):turnturnturnwhile

//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)

然而,这并不能确保“互斥”属性:例如,当 P0 位于其临界部分时,P1 可以愉快地设置并继续其临界部分。turn=1

吸取的教训是:在并发方面要非常小心。到处都有出错的机会,在编码时很难看到,之后更难排除故障。只要有可能,就采用已建立的方法,如彼得森算法,这些方法多年来已经过彻底的分析和优化。