像许多其他人一样,我一直对易失性读/写和栅栏感到困惑。所以现在我试图完全理解这些是做什么的。
因此,volatile 读取应该 (1) 表现出获取语义和 (2) 保证读取的值是新鲜的,即,它不是缓存值。让我们关注(2)。
现在,我已经读到,如果您想执行易失性读取,您应该在读取之后引入一个获取栅栏(或完整栅栏),如下所示:
int local = shared;
Thread.MemoryBarrier();
这究竟如何防止读取操作使用先前缓存的值?根据栅栏的定义(不允许在栅栏上方/下方移动读取/存储),我会在读取之前插入栅栏,防止读取穿过栅栏并及时向后移动(又名,被缓存)。
防止读取及时向前移动(或后续指令及时向后移动)如何保证易失性(新)读取?它有什么帮助?
同样,我认为 volatile 写入应该在写入操作之后引入栅栏,防止处理器及时向前移动写入(也就是延迟写入)。我相信这会使处理器刷新对主存的写入。
但令我惊讶的是,C# 实现在写入之前引入了栅栏!
[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;
}
更新
根据这个例子,显然取自“C# 4 in a Nutshell”,放置在写入之后的栅栏 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
实现背后的想法相矛盾。