也许我过于简单化了,但我认为关于重新排序和缓存一致性等的解释提供了太多细节。
那么,为什么 MemoryBarrier 在实际读取之后出现?
我将尝试通过使用 object 而不是 int 的示例来解释这一点。
可能有人认为正确的是:
线程 1 创建对象(初始化其内部数据)。
线程 1 然后将对象放入一个变量中。
然后它“做一个栅栏”,所有线程都会看到新值。
然后,读取是这样的:
线程 2“做一个栅栏”。
线程 2 读取对象实例。
线程 2 确信它拥有该实例的所有内部数据(因为它以栅栏开头)。
这样做最大的问题是:
线程 1 创建对象并对其进行初始化。
线程 1 然后将对象放入一个变量中。
在线程刷新缓存之前,CPU 本身会刷新缓存的一部分……它只提交变量的地址(而不是该变量的内容)。
此时,线程 2 已经刷新了它的缓存。所以它将从主存储器中读取所有内容。
所以,它读取变量(它在那里)。
然后它读取内容(它不存在)。
最后,在这一切之后,CPU 1 执行了执行栅栏的线程 1。
那么,易失性写入和读取会发生什么?
易失性写入使对象的内容立即进入内存(从栅栏开始),然后设置变量(可能不会立即进入真实内存)。
然后,易失性读取将首先清除缓存。然后它读取字段。如果它在读取字段时接收到一个值,则可以确定该引用所指向的内容确实存在。
通过这些小事,是的,有可能您执行了 VolatileWrite(1) 并且另一个线程仍然看到零值。但是一旦其他线程看到 1 的值(使用 volatile 读取),所有其他可能被引用的项目都已经存在。您无法真正告诉它,因为在读取旧值(0 或 null)时,考虑到您仍然没有所需的一切,您可能根本没有进步。
我已经看到一些讨论,即使刷新缓存两次,正确的模式将是:
MemoryBarrier - 将刷新在此调用之前更改的其他变量
写
MemoryBarrier - 将保证写入已刷新
Read 将需要相同的内容:
记忆屏障
阅读 - 保证我们看到最新的信息……也许是放在我们的记忆障碍之后的信息。
由于我们的 MemoryBarrier 之后可能出现了某些内容并且已经被读取,因此我们必须放置另一个 MemoryBarrier 来访问内容。
如果存在于 .Net 中,它们可能是两个 Write-Fences 或两个 Read-Fences。
我不确定我所说的一切...这是对我获得的许多信息的“汇编”,它确实解释了为什么 VolatileRead 和 VolatileWrite 似乎被颠倒了,但它也保证在以下情况下不会读取无效值使用它们。