来自O'Reilly的C#in a Nutshell:
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);
}
}
}
假设方法A和B在不同的线程上并发运行:
作者说:“障碍1和4阻止这个例子写”0“。障碍2和3提供了一个 新鲜度保证:他们确保如果B在A之后运行,则读取_complete将进行评估 为了真实。“
我的问题是:
答案 0 :(得分:6)
内存屏障强制对来自内存的读写进行排序约束:在屏障发生之前的内存访问操作 - 在屏障之后的内存访问之前。
障碍1和障碍4具有互补作用:障碍1确保在写入_answer
之前发生写入到_complete
,而障碍4确保来自_complete
的读取发生在从_answer
读取之前。想象屏障4不存在,但屏障1是。虽然可以保证123
在_answer
写入true
之前写入_complete
,但其他一些运行B()
的线程可能仍会重新排序其读取操作,因此在阅读_answer
之前,它可能会显示_complete
。类似地,如果屏障1被移除,屏障4被保留:_complete
中B()
的读取将始终发生 - 在从_answer
读取之前,_complete
仍然可以写入运行_answer
的其他线程在A()
之前。
障碍2和3提供了新鲜度保证:如果屏障3在屏障2之后执行,那么在执行屏障2时运行A()
的线程可见的状态对于运行{ {1}}在执行屏障3时,如果没有任何这两个障碍B()
在B()
完成后执行可能看不到A()
所做的更改。特别是屏障2阻止写入A()
的值被运行_complete
的处理器缓存,并强制处理器将其写入主存储器。类似地,屏障3阻止处理器运行A()
依赖于高速缓存的值B()
强制从主存储器读取。但请注意,在没有内存屏障2和3的情况下,过时缓存不是唯一可以防止新鲜度保证的事情。内存总线上的操作重新排序是这种机制的另一个例子。
内存屏障只是确保跨屏障排序内存访问操作的效果。其他指令(例如递增寄存器中的值)仍然可以重新排序。
答案 1 :(得分:1)
好的,我们走了: 内存屏障阻止优化编译器重新排序指令。这意味着在屏障之后的指令之后,不能在屏障之前执行指令。有几种类型的障碍,但我不会详述。此外,具有弱内存排序的CPU可以重新排序指令并可以创建死锁。所以: