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