ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

单标志法,双标志先检查法,双标志后检查法,Peterson算法,进程互斥的软件解决方法

2026/8/15 19:39:10 拓冰建站 浏览量
单标志法,双标志先检查法,双标志后检查法,Peterson算法,进程互斥的软件解决方法

用那个生动有趣的比喻来讲解,这次的场景是:两个人(P0, P1)抢着用同一个卫生间

  • 卫生间:临界资源。
  • 进入卫生间:访问临界区。
  • 目标:设计一套规则,保证任何时候最多只有一个人在卫生间里,而且规则要公平高效。

第一代方案:单标志法 (Single Flag Method) - “卫生间门上只挂一把钥匙”

这个方案非常简单,就像卫生间门上只挂了一把钥匙,谁拿到钥匙谁就能进。

  • 规则

    1. 设立一个公共变量 turn,它就像那把唯一的“钥匙”。turn = 0 表示该轮到 P0 用;turn = 1 表示该轮到 P1 用。
    2. P0 想用卫生间时,会反复检查 turn 是不是等于 0。如果是,就进去。用完出来后,他必须把 turn 改成 1,把“钥匙”交给 P1。
    3. P1 同理。
  • 问题出在哪?

    • 这个方法确实保证了“互斥”,因为钥匙只有一把,不可能两个人同时拿到。
    • 但是,它违反了“空闲让进”原则
    • 场景:假设 P0 用完卫生间,把钥匙给了 P1 (turn 变成 1)。但 P1 现在根本不想上厕所,他跑去客厅看电视了。此时,P0 又想上厕所了,他跑到门口一看,钥匙不在 (turn 不等于 0),他只能在门口干等着。尽管卫生间是空的,但因为“使用权”在 P1 手里,P0 也进不去。这就很不合理!

一句话总结:强行轮流,缺乏灵活性。


第二代方案:双标志先检查法 (Double Flags, Check First) - “先看一眼对方,再决定去不去”

吸取了上一个方案的教训,我们决定不用“钥匙”了,而是每个人自己表明状态。就像每个人头上都顶了个牌子,写着“想去”或“不想去”。

  • 规则

    1. 设立一个布尔数组 flag[2]flag[i] = true 表示 Pi“想去”卫生间。
    2. P0 想用卫生间时,他会先看一眼 P1 头上的牌子 (flag[1])。如果 P1 写的是“不想去”,P0 觉得“太好了,机会来了!”,于是他立刻把自己的牌子改成“想去”(flag[0] = true),然后冲进卫生间。
    3. P1 同理。
  • 问题出在哪?

    • 这个方案看起来很美好,但它在并发环境下会出大问题,违反了“忙则等待”原则
    • 场景(致命的巧合)
      1. 在某一瞬间,P0 看了 P1 的牌子,发现是“不想去”。
      2. 就在 P0 准备举起自己“想去”的牌子、但还没举起来的那一刹那,系统发生了一次切换,轮到 P1 执行。
      3. P1 也看了 P0 的牌子,发现也是“不想去”(因为 P0 还没来得及改)。
      4. 于是 P1 也觉得机会来了,把自己牌子改成“想去”,然后准备冲进去。
      5. 现在,P0 和 P1 都认为对方不想去,并且都把自己标记为“想去”,结果两个人可能同时冲进了卫生间!出大事了。

一句话总结:“检查”和“上锁”不是原子操作,导致互斥失败。


第三代方案:双标志后检查法 (Double Flags, Set First) - “先占坑,再看对方”

为了解决上一个方案的问题,我们改变了顺序:不管三七二十一,先表明我想去,再看对方。

  • 规则

    1. 还是用 flag[2] 数组。
    2. P0 想用卫生间时,他二话不说,先把自己的牌子改成“想去” (flag[0] = true)。
    3. 然后,他才去看 P1 的牌子 (flag[1])。如果 P1 的牌子也是“想去”,P0 就会在门口等着。如果 P1 是“不想去”,P0 才进去。
    4. P1 同理。
  • 问题出在哪?

    • 这个方案确实解决了两个人同时冲进去的问题。因为如果两人同时想去,他们会同时举起“想去”的牌子,然后互相看着对方的牌子,发现对方也想去,于是谁也不敢动。
    • 但这就导致了新的问题:“死锁”或“饥饿”违反了“空闲让进”和“有限等待”原则
    • 场景:P0 和 P1 同时想去卫生间。他们同时把自己的牌子改成“想去”。然后 P0 看 P1,发现 P1 想去,于是 P0 等待。P1 看 P0,发现 P0 也想去,于是 P1 也等待。结果两个人就在卫生间门口互相谦让,“你先请,你先请”,谁也进不去,尽管卫生间是空的。

一句话总结:过度谦让,导致“活锁”(两人都在动,但事情没进展)。


第四代方案:Peterson算法 (The Genius Solution) - “意愿+谦让,完美结合”

终于,一位叫 Peterson 的大神站了出来,他巧妙地把“单标志法”(钥匙)和“双标志法”(牌子)结合起来,解决了所有问题。

  • 规则

    1. 既有“牌子”,又有“钥匙”。我们有 flag[2] 数组(表示意愿),还有一个 turn 变量(表示谁该谦让)。
    2. P0 想用卫生间时,他会做三件事: a. 表明意愿:把自己牌子改成“想去”(flag[0] = true)。 b. 主动谦让:把“钥匙”交给对方,说“你先请吧”(turn = 1)。 c. 等待条件:然后他就在门口循环等待,直到满足以下任一条件才进去: * 对方 P1 根本就不想去 (flag[1] == false)。 * 或者,虽然 P1 也想去,但“钥匙”在自己手里 (turn == 0)。(意思是,虽然我刚才谦让了,但对方如果也谦让了,把钥匙又给了我,那还是我进吧)。
    3. P1 同理。
  • 为什么它这么牛?

    • 保证互斥:如果两人都想进,flag 都为 true。但 turn 要么是0要么是1,不可能同时是两者。假设 turn 最后被设为1,那么 P0 的等待条件 (flag[1]==true && turn==1) 为真,P0会等待。而 P1 的等待条件 (flag[0]==true && turn==1) 为假,P1可以进入。所以不会同时进入。
    • 保证空闲让进:如果 P1 不想进,flag[1] 为 false,P0 的等待条件直接不满足,P0 可以立刻进入。
    • 保证有限等待:不会出现两人互相谦让的情况。因为 turn 变量明确了最终的裁决权,总有一个人需要等待,另一个人可以进入。等待者最多只需等对方使用完一次卫生间即可。
  • 小小的缺点

    • 它没有实现“让权等待”。当 P0 进不去时,他是在门口不停地检查条件(while 循环),这个过程会一直占用 CPU,造成“忙等待”。

一句话总结:通过“举牌表明意愿,递钥匙主动谦让”的机制,完美地解决了前三个方案的所有问题(除了忙等待)。

这就是软件层面解决互斥问题的思维演进过程,从简单到复杂,再到精巧,每一步都是对前一步问题的修复和思考。