MemoryBarrier是否保证所有内存的内存可见性?

adv*_*v12 3 c# multithreading

如果我理解正确,在C#中,lock块保证对一组指令的独占访问,但它也保证从内存中的任何读取都反映了任何CPU缓存中该内存的最新版本.我们认为lock块是保护块内读取和修改的变量,这意味着:

  1. 假设您已经在必要时正确实现了锁定,那么这些变量一次只能被一个线程读取和写入,并且
  2. lock块内的读取参见变量的最新版本,lock块内的写入对所有线程都可见.

(对?)

第二点是我感兴趣的.是否存在一些魔力,只有在lock块保护的代码中读取和写入的变量才能保证新鲜,或者在实现中使用的内存屏障是否lock保证所有内存现在对所有线程都同样新鲜?请原谅我在这里关于缓存是如何工作的心理模糊性,但我已经读过缓存存有几个多字节"数据线"的数据.我想我要问的是,内存屏障是否强制同步所有 "脏"缓存行或只是一些,如果只是一些,是什么决定了哪些行同步?

Eri*_*ert 7

如果我理解正确,在C#中,锁定块可以保证对一组指令的独占访问权限......

对.规范保证.

但它也保证了来自内存的任何读取都反映了任何CPU缓存中该内存的最新版本.

C#规范对"CPU缓存"没有任何说明.您已经离开了规范所保证的范围,并进入了实现细节的领域.不要求C#的实现在具有任何特定缓存体系结构的CPU上执行.

是否存在一些魔法,只有在锁定块保护的代码中读取和写入的变量才能保证新鲜,或者锁定实现中使用的内存屏障是否保证所有内存现在对所有线程都同样新鲜?

而不是试图解析你的问题或者问题,让我们说一下语言实际保证了什么.特效是:

  • 对变量的任何写入,是否为volatile
  • 任何易失性字段的读取
  • 任何投掷

在某些特殊点保留特殊效果的顺序:

  • 读取和写入易失性字段
  • 线程创建和终止

运行时需要确保特殊点的特殊效果一致.因此,如果在锁定之前读取易失性字段,并且在写入之后读取,则在写入之后不能移动读取.

那么,运行时如何实现这一目标呢?打败了我.但运行时肯定不需要"保证所有线程的所有内存都是新鲜的".运行时需要确保某些读取,写入和抛出按特定点的时间顺序发生,这就是全部.

特别地,运行时并不要求所有线程遵循相同的顺序.

最后,我总是通过指向你来结束这些讨论:

http://blog.coverity.com/2014/03/26/reordering-optimizations/

阅读完之后,你应该欣赏即使在x86上你可能会偶然发生关于锁定的各种可怕的事情.


Jon*_*eet 6

锁块内的读取参见变量的最新版本,并且锁块内的写入对所有线程都可见.

不,这绝对是一个有害的过度简化.

当你输入lock的语句,还有一个内存围栏这有点意思,你永远读"新鲜"的数据.当你退出的lock状态,有一个内存围栏这有点意味着所有你写了保证数据被写入到主内存和提供给其他线程.

重要的一点是,如果多个线程只有在"拥有"特定锁时才读/写内存,那么根据定义,其中一个将在下一个进入之前退出锁...所以所有这些读写都将简单而正确.

如果你有代码在没有锁定的情况下读取和写入变量,那么就不能保证它会"看到"由行为良好的代码(即使用锁的代码)写入的数据,或者表现良好的线程将"看到"这个坏代码写的数据.

例如:

private readonly object padlock = new object();
private int x;

public void A()
{
    lock (padlock)
    {
        // Will see changes made in A and B; may not see changes made in C
        x++;
    }
}

public void B()
{
    lock (padlock)
    {
        // Will see changes made in A and B; may not see changes made in C
        x--;
    }
}

public void C()
{
    // Might not see changes made in A, B, or C. Changes made here
    // might not be visible in other threads calling A, B or C.
    x = x + 10;
}
Run Code Online (Sandbox Code Playgroud)

现在它比这更微妙,但这就是为什么使用公共锁来保护一组变量的原因.

  • @ adv12:而不是推断CPU级别实际发生的事情,考虑保证什么.在C中读取x不是"特殊事件",因此可以相对于任何其他读取和写入任意地重新排序.(好吧,除了用C写的文字!显然那里有数据依赖.) (2认同)