答案 0 :(得分:2)
遵守因果关系意味着如果事物A导致另一件事B,并且如果你发现B已经发生,那么你也会发现A已经发生了。请考虑以下代码:
x = 0
y = 0
thread 1 thread 2
x = 10 r1 = y // #1
y = 25 r2 = x // #2
假设所有读取和写入都是单独正确的(即在指令级别没有撕裂的原子,例如在C ++中使用宽松原子),请考虑线程2在点#1
处对世界的看法。
如果r1
为零,那么我们什么都不知道。线程1的执行可以是任何地方;它可能还没有开始,或者它可能已经完成但是变化还不可见。
但是,如果r1
为25,那么通过因果关系,我们知道r2
必须被读为10,因为{{1}的唯一方式如果线程1已经执行了两个语句,则可以读取25,并且x86的强内存排序可以保证先前(因果关系前面)存储的效果可见。
请注意,这不是所有硬件的一般功能。在流行的当代内存模型(例如C ++ 11,C11,Java和Go)中,我们会说在上面的操作中,商店r1
具有“发布排序”和负载{{1}有“获取订购”,两个操作“同步”。
答案 1 :(得分:2)
在现代处理器上,在处理器执行需要该值的指令之前很久就会发生内存读取。预取器的工作,它确保处理器不会停止等待非常慢的内存读取完成。并且在处理器执行更新值的指令之后很久就会发生对存储器的写入。写回缓冲区的工作,它确保处理器可以继续执行指令,并且不必等待非常慢的内存写入完成。
这可能会导致排序问题,预取程序可能会在读取变量A之前读取变量B的内存但处理器实际上可能在使用A之前使用A.对于写回缓冲区,它可以执行内存写入的顺序与处理器执行了更新。
当另一个线程使用那些相同的值时,事情会出错,它可以看到A的陈旧值,但是B的更新值,即使程序在B之前更新了A。这是一个错误的' s几乎不可能诊断,这是因果关系违规。一个昂贵的词只是意味着"这没有什么特别的意思!",当您调试多线程程序时看到这种情况发生的更典型的感叹。
x86和x64架构具有强大的内存模型,它承诺不会发生这种排序问题。其他体系结构(如ARM和Itanium)很弱,需要明确的排序指令。弱存储器模型是理想的,因为它可以更快并且需要更少的功率。只有处理器设计师喜欢它们,程序员才讨厌它们。但是,您很少会明确地依赖它,您使用语言运行时或操作系统提供的同步原语来确保良好的心理健康。