Appearance
题目
有两个并发执行的进程 P1 和 P2,共享初值为 1 的变量 x。P1 对 x 加 1,P2 对 x 减 1。加 1 和减 1 操作的指令序列分别如下所示。
C
P1 // 加 1 操作
load R1, x // 取 x 到寄存器 R1 中
inc R1
store x, R1 // 将 R1 的内容存入 x
P2 // 减 1 操作
load R2, x // 取 x 到寄存器 R2 中
dec R2
store x, R2 // 将 R2 的内容存入 x两个操作完成后,x 的值( )。
错因
A
凑了"两次加"或"两次减"的极端值——但每次操作内部 inc / dec 只执行一次,最终结果不可能比初值大 2 或小 2。最终 x 的值由"最后一次 store 写入的值"决定,而每个 R 的范围是 0~2,store 不可能写出 -1 或 3。
B
把"互斥执行"和"交错执行"混了——只有当两个操作互斥执行(P1 完才 P2、或 P2 完才 P1)时,结果才一定是 1。题目说并发执行,没有互斥保护,指令可以交错,结果不止 1。"只能为 1"是假设了互斥但题面没说。
D
把 -1 也算进去——但分析下来 x 不会是 -1。即使最坏交错,store 写入的也只能是 R1(0/1/2 之一)或 R2(0/1/2 之一),最终值只可能是 0、1、2。把 -1 当作可能值是没仔细枚举每条指令的取值范围。
总解析
每个操作的内部指令是顺序的:
| P1 | P2 |
|---|---|
| load R1, x | load R2, x |
| inc R1 | dec R2 |
| store x, R1 | store x, R2 |
并发执行时两个操作的 6 条指令可以任意交错,但每个操作内部顺序保持。最终 x 值由最后一次 store 决定。
枚举可能结果:
情况 1:完全串行(P1 全完再 P2,或反之)
- P1 → P2:x: 1 → 2 → 1。最终 x = 1
- P2 → P1:x: 1 → 0 → 1。最终 x = 1
情况 2:P1 store 后 P2 store(两边都 load 到 1,但 P1 先写)
- P1 load R1=1 → inc R1=2 → store x=2
- 然后 P2 load R2=1 → dec R2=0 → store x=0
- 最终 x = 0
这种情况要 P2 的 load 在 P1 的 store 之后。
情况 3:P1 load 之后 P2 全完,再 P1 store(P1 用旧值覆盖 P2 的写)
- P1 load R1=1 → P2 load R2=1 → P2 dec R2=0 → P2 store x=0 → P1 inc R1=2 → P1 store x=2
- 最终 x = 2
情况 4:交错但都读到旧值
- P1 load R1=1 → P2 load R2=1 → P1 inc → P2 dec → 两边各自 store
- 若 P1 后 store:x = 2
- 若 P2 后 store:x = 0
总结:
| 交错模式 | x 最终值 |
|---|---|
| 串行(任一顺序) | 1 |
| 一边完后另一边读到新值(也是串行情况) | 1 |
| 两边都读到旧值 1,P2 后 store | 0 |
| 两边都读到旧值 1,P1 后 store | 2 |
可能值集合:{0, 1, 2}。
为什么不可能是 -1 或 3:
- store 的值来自 R1 或 R2,范围分别是 [0, 2](R2 = 1-1=0;R1 = 1+1=2)
- 两个寄存器都基于初始 x = 1 计算(最早只能 load 到 1,因为没人改过 x;中途 store 后另一方 load 才可能拿到 0 或 2)
- 即使中途 store 改了 x,另一方再 load 拿到 0 或 2,也只能进一步算到 -1 或 3(dec(0)=-1, inc(2)=3)——但要让 P2 在 P1 store(x=2) 之后重新 load + dec,需要 P2 已经完成 load 再回去,但每个操作只 load 一次,不可能重新 load。所以 -1 / 3 排除。
最终答案是 C。