了解非阻塞线程同步和Thread.MemoryBarrier

Val*_*zub 5 .net c# multithreading

在这个线程在线书籍:http: //www.albahari.com/threading/part4.aspx

这是一个例子 Thread.MemoryBarrier()

   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)

我们讨论了是否有线程阻塞正在进行?

我认为有一些,特别是考虑到了

在2010年代的桌面上,完整的围栏需要大约10纳秒.

另一方面,完全围栏只应该disable instructions reodering and caching通过它的声音不符合线程阻塞的条件,(lock与其清除线程等待其他人在继续之前释放锁定并且在此期间被阻止的情况不同)

关于那个线程'阻止状态'.我说的不是线程是否被置于阻塞状态,而是是否有一些线程同步发生,这意味着一个线程无法运行,而其他线程不允许它这样做,通过MemoryBarrier in这个案例.

我也希望清楚地了解每个障碍实现的目标.例如Barrier 2 - 它如何提供新鲜度保证以及它如何连接到屏障3?如果有人会在这里详细解释每个障碍的目的(如果不存在1或2或3或4那么可能会出错)我认为id会大大提高我对此的理解.

编辑:它现在大部分都清楚了1,2和3.然而,3不具备的4还不清楚.

dle*_*lev 5

指令花费时间执行的事实并不意味着线程被阻止.当一个线程被特定地置于阻塞状态时被阻塞,但是MemoryBarrier()没有.

实际上阻​​止指令重新排序和缓存刷新的处理器指令需要时间,因为它们必须等待缓存再次变得连贯.在此期间,线程仍被视为正在运行.

更新:让我们看看示例中实际发生了什么,以及每个内存屏障实际上做了什么.

正如链接所说,1和4确保产生正确的答案.这是因为1确保将答案刷新到内存中,并且4确保在检索变量之前刷新读取缓存.

2和3确保如果A先运行,那么B始终打印答案.Barrier 2确保将写入true刷新到内存,屏障3确保在测试_complete值之前刷新读取cahces .

缓存和内存刷新应该足够清晰,所以让我们看看指令重新排序.编译器,CLR和CPU知道它们可以重新排序指令的方式是按顺序分析一组指令.当他们在序列中间看到屏障指令,他们知道指令不能跨越该边界.这确保除了缓存新鲜度之外,指令以正确的顺序发生.