给出以下代码:
int x = 0;
co x:= x + 1 || x := x - 1 oc
显然,可能的值为x = {0, -1, 1}
。
我试图了解这是如何发生的,并且我读到有关原子弹的事,只有它们发生时,它们才会发生。
在本书中,给出了一个示例,我们可以将语句x := x + 1
分解为原子语句,例如。
READ X (R1)
INC
WRITE
(W1)
和
x := x - 1
进入:
READ X (R2)
DEC
WRITE
(W2)
它说INC WRITE
和READ inc
的行为都好像是原子的,我真的不明白为什么做这个假设是安全的吗?
然后,看来该程序顺序返回值-1
:R1 -> R2 -> W1 -> W2
,我不明白为什么?
据我所知,似乎我们正在读取x
的值,然后再次读取它,然后再使其减一,然后再去减一,这不应该等于0
?
感谢帮助。
答案 0 :(得分:0)
并发问题是您无法控制并发访问的顺序。因此,我们做出以下假设:
-x是由不同线程访问的全局变量
-x ++和x--由独立线程执行。
如您的书所述,对共享变量执行读-修改-写是一个三步过程:
1 /将var读入处理器reg
2 /修改处理器规则
3 /将处理器寄存器写入寄存器
这些单独的动作中的每一个对应于处理器指令,因此是 atomic 。但是全局读-修改-写操作不是,可以被其他并发操作拆分。
现在考虑并发操作
Thread A Thread B
1/ read x->regA a/ read x->regB
2/ regA++ b/ regB--
3/ write regA->x c/ write regB
在一个线程内,将遵循操作顺序123或abc,但是在线程A和B之间,任何相对顺序都是可能的。
有了123abc,我们得到了预期的结果。 x由线程A更改为1,然后由线程B读取,递减,最后x = 0
abc123相同
但是其他排序将导致不同的结果。 例如:1a2b3c
Thread A Thread B
1/ read x->regA=0
a/ read x->regB=0
2/ regA++=1
b/ regB--=-1
3/ write regA->x=1
c/ write regB->x=-1
最后是x = -1。
但是如果订购1abc23,我们将得到x = 1。
Thread A Thread B
1/ read x->regA=0
a/ read x->regB=0
b/ regB--=-1
c/ write regB->x=-1
2/ regA++=1
3/ write regA->x=1
所有订单均可以到达,结果将为\ in {-1,0,1}
获得确定性结果的唯一方法是使用原子读取-修改-写入指令。所有现代处理器都具有执行此操作的手段(获取和添加或加载链接/存储条件),在这种情况下,无法拆分序列123或abc。唯一可能的排序将是123abc或abc123,并且两者都会进行x = 0的确定性结果。