需要澄清Thread.MemoryBarrier()

Tar*_*Tar 3 c# multithreading memory-barriers

可能重复:
为什么我们需要Thread.MemoryBarrier()?

来自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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

假设方法A和B在不同的线程上并发运行:


作者说:"障碍1和4阻止这个例子写"0".障碍2和3提供了新鲜度保证:他们确保如果B在A之后运行,则读取_complete将评估为真.

我的问题是:

  1. 为什么需要Barrier 4?障碍1还不够?
  2. 为什么需要2和3?
  3. 根据我的理解,屏障阻止在按照以下说明进行操作之前执行指令,我是否正确?

Ada*_*man 6

内存屏障强制对来自内存的读写进行排序约束:在屏障发生之前的内存访问操作 - 在屏障之后的内存访问之前.

  1. 壁垒1和4具有互补作用:屏障1确保了_answer之前发生的写入_complete,而屏障4确保读取_complete之前发生从读取_answer.想象屏障4不存在,但屏障1是.虽然保证123写入_answer之前true写入_complete其他一些线程,但运行B()仍然可以重新排序其读取操作,因此它可能在读取_answer之前读取_complete.同样,如果屏障1与屏障4删除保存:当从读_completeB()总是会发生,从之前的读取_answer,_complete仍然可以写入之前_answer从其他线程运行A().

  2. 障碍物2和3提供了新鲜度保证:如果障碍物3在障碍物2之后执行,那么A()当它执行障碍物2时,在其执行障碍物2的点处运行的线程可见的状态变得对于在B()执行障碍物3时运行的线程可见.完成B()后执行的这两个障碍中的任何一个都A()可能看不到所做的更改A().特别是屏障2防止写入的值_complete被处理器运行缓存A()并强制处理器将其写入主存储器.类似地,屏障3防止处理器运行B()依赖于高速缓存来_complete强制从主存储器读取的值.但是请注意,在没有内存屏障2和3的情况下,过时缓存不是唯一可以防止新鲜度保证的事情.对内存总线上的操作进行重新排序是这种机制的另一个例子.

  3. 内存屏障只是确保内存访问操作的效果跨屏障排序.其他指令(例如,递增寄存器中的值)仍然可以重新排序.