锁定是不是确保从缓存中刷新读取和写入?如果是这样,怎么做?
Posted
技术标签:
【中文标题】锁定是不是确保从缓存中刷新读取和写入?如果是这样,怎么做?【英文标题】:Does locking ensure reads and writes are flushed from caches? If so, how?锁定是否确保从缓存中刷新读取和写入?如果是这样,怎么做? 【发布时间】:2011-12-11 14:51:04 【问题描述】:我正在阅读this MSDN article on lockless thread syncing。该文章似乎推断,只要您在访问共享变量之前输入锁,那么这些变量将是最新的(至少在 .Net 2.0 中)。
我开始思考这怎么可能? .Net 中的锁只是所有线程在访问内存之前检查的任意对象,但锁本身不知道正在访问的内存位置。
如果我有一个线程更新一个变量,甚至是整个内存块,当进入/退出锁时,如何保证这些更新从 CPU 缓存中刷新?是否所有内存访问都有效地在锁内变为易失性?
【问题讨论】:
这不是我有太多经验的领域,但为什么你访问的内存位置是否在 CPU 缓存中很重要。 @BenRobinson - 假设您有 2 个线程在不同的内核上运行,访问堆上的 1 个整数。在这种情况下,除非应用了适当的同步方法,否则每个线程都可能在本地内核的缓存中存储该值的副本。 【参考方案1】:查看 Eric Lippert 的工作:http://blogs.msdn.com/b/ericlippert/archive/2011/06/16/atomicity-volatility-and-immutability-are-different-part-three.aspx
锁保证在锁内读取或修改的内存被观察到是一致的,锁保证一次只有一个线程访问给定的内存块,等等。
所以是的,只要你每次在访问共享资源之前都加锁,你就可以确定它是最新的
编辑查看以下帖子以获取更多信息和非常有用的概述:http://igoro.com/archive/volatile-keyword-in-c-memory-model-explained/
【讨论】:
有趣的链接,谢谢。没有意识到所有 c# 写入都是易变的!第二个链接回答了我的问题。内存访问在锁内不是易失性的,但释放锁会刷新写入,获得锁会刷新读取缓存,因此值是最新的。 这是指 .net 提供的所有类型的锁,包括 Mutex、Monitor、ReaderWriterLock 等吗?【参考方案2】:好吧,文章解释了它:
读入锁之前不能移动。
退出锁定后写入无法移动。
同一篇文章的更多解释:
当线程退出锁时,第三条规则确保在持有锁期间所做的任何写入对所有处理器都是可见的。在另一个线程访问内存之前,读取线程会进入一个锁,第二条规则确保读取在逻辑上发生在获得锁之后。
【讨论】:
【参考方案3】:并非所有 c# 内存读取和写入都是易失性的,不是。 (想象一下,如果在性能方面是这样的话!)
但是。
当进入/退出锁时,这些更新如何保证从 CPU 缓存中刷新
CPU 缓存是特定于 CPU 的,但是它们都有某种形式的 memory coherence protocol。也就是说,当你从一个内核访问一些内存时,如果它存在于另一个内核缓存中,CPU 使用的协议将确保数据被传递到本地内核。
Petar Ivanov 在他的回答中暗示的内容非常相关。如果您想更多地了解他的观点,您应该查看memory consistency model。
现在,C# 如何保证内存是最新的取决于 C# 实现者,而 Eric Lippert 的博客无疑是了解潜在问题的好地方。
【讨论】:
【参考方案4】:我不确定 .NET 中的情况,但在 Java 中明确规定,以这种方式协作的任何两个线程都必须使用 same 对象进行锁定,以便受益于您在介绍性声明中所说的内容,而不仅仅是任何锁。这是一个至关重要的区别。
锁不需要“知道”它保护什么;它只需要确保前一个储物柜写入的所有内容都可供另一个储物柜使用,然后再让它继续。
【讨论】:
以上是关于锁定是不是确保从缓存中刷新读取和写入?如果是这样,怎么做?的主要内容,如果未能解决你的问题,请参考以下文章
如何在 C# 中清空/刷新 Windows READ 磁盘缓存?