像许多其他人一样,我一直对易失性读/写和围栏感到困惑。所以现在我想完全理解这些是做什么的。
因此,易失性读取应该(1)表现出获取语义,(2)保证读取的值是新鲜的,即它不是缓存值。让我们关注(2)。
现在,I've read,如果你想执行易失性读取,你应该在读取后引入一个获取栅栏(或一个完整的栅栏),如下所示:
int local = shared;
Thread.MemoryBarrier();
这究竟是如何阻止读取操作使用以前缓存的值? 根据围栏的定义(不允许读取/存储在围栏上方/下方移动),我会在读取之前插入围栏,防止读取越过围栏并被移动向后及时(又名,被缓存)。
如何防止读取及时向前移动(或后续指令不及时向后移动)可以保证读取不稳定(新鲜)?它有什么用?
类似地,我认为易失性写入应该在写入操作之后引入一个fence ,从而阻止处理器及时向前移动写入(也就是说,延迟写入)。我相信这会使处理器刷新对主存储器的写入。
但令我惊讶的是,C# implementation在写之前引入了围栏!
[MethodImplAttribute(MethodImplOptions.NoInlining)] // disable optimizations
public static void VolatileWrite(ref int address, int value)
{
MemoryBarrier(); // Call MemoryBarrier to ensure the proper semantic in a portable way.
address = value;
}
更新
根据this example显然取自“Nutshell中的C#4”,围栏2,在写入之后放置应该强制写入立即刷新到主内存,并且在读取之前放置的围栏3应该保证新的读取:
class Foo{
int _answer;
bool complete;
void A(){
_answer = 123;
Thread.MemoryBarrier(); // Barrier 1
_complete = true;
Thread.MemoryBarrier(); // Barrier 2
}
void B(){
Thread.MemoryBarrier(); // Barrier 3;
if(_complete){
Thread.MemoryBarrier(); // Barrier 4;
Console.WriteLine(_answer);
}
}
}
本书中的想法(以及我自己的个人信仰)似乎与C#的VolatileRead
和VolatileWrite
实现背后的想法相矛盾。
答案 0 :(得分:9)
这究竟是如何阻止读取操作使用a 以前缓存过的值?
没有这样的事情。易失性读取不保证将返回最新值。简单地说,所有它的意思是下一次读取将返回一个更新的值,而不是更多。
如何防止读取及时向前移动(或 随后的指示被及时向后移动保证 一个不稳定(新鲜)的读物?它有什么用?
请注意这里的术语。挥发性不是新鲜的同义词。正如我上面已经提到的,它的真正用处在于如何将两个或多个易失性读取链接在一起。一系列易失性读取中的下一次读取将绝对返回较新的值,而不是先前读取的相同地址。应该在考虑此前提的情况下编写无锁代码。也就是说,代码的结构应该适用于处理较新值而不是最新值的原则。这就是为什么大多数无锁代码在循环中旋转,直到它可以完全验证操作成功。
本书中的想法(以及我自己的个人信仰)似乎如此 与C#的VolatileRead和VolatileWrite背后的想法相矛盾 的实施方式。
不是真的。记得不稳定!=新鲜。是的,如果你想要一个新鲜的"然后读取你需要在读取之前放置一个获取栅栏。但是,这与进行易失性读取不同。我所说的是,如果VolatileRead
的实现在读取指令之前调用了Thread.MemoryBarrier
,那么它实际上不会产生 volatile 读。如果会产生 fresh 读取。
答案 1 :(得分:6)
要理解的重要一点是volatile
不仅意味着“不能缓存值”,而且还提供了重要的可见性保证(确切地说,完全有可能只有缓存的易失性写入;仅取决于硬件及其使用的缓存一致性协议)
易失性读取提供获取语义,而易失性写入具有释放语义。获取围栏意味着您无法在围栏之前重新排序读取或写入,而释放围栏意味着您无法在围栏之后移动它们。 linked answer in the comments解释说实际上相当不错。
现在的问题是,如果我们在加载之前没有任何内存障碍,我们将如何确保看到最新值?答案是:因为我们在每次volatile写操作之后也会设置内存屏障来保证这一点。
Doug Lea写了一篇很好的总结,介绍了哪些障碍存在,他们做了什么以及将它们放在哪里用于JMM的易失性读/写作为编译器编写者的帮助,但该文本对其他人也非常有用。易失性读写在Java和CLR中都提供了相同的保证,因此通常适用。Source - 向下滚动到“Memory Barriers”部分(我会复制有趣的部分,但格式化不存在......)