锁定是否确保读写操作已经刷新缓存?如果是,怎样实现的?

8

我在阅读这篇关于无锁线程同步的MSDN文章。该文章似乎暗示着只要在访问共享变量之前进入锁定状态,那么这些变量将是最新的(至少在.Net2.0中)。

我开始思考这是如何可能的?在.Net中,锁仅是一些任意对象,所有线程在访问内存之前都会检查它,但锁本身并不知道正在访问的内存位置。

如果有一个线程更新变量,甚至是整个内存块,当进入/退出锁定状态时,如何保证这些更新被刷新出CPU缓存?所有内存访问是否在锁定期间有效地变为易失性?


这不是我非常熟悉的领域,但为什么访问的内存位置是否在CPU缓存中很重要呢? - Ben Robinson
@BenRobinson - 假设你有两个线程在不同的核心上运行,访问堆上的一个整数。在这种情况下,除非应用适当的同步方法,否则每个线程可能会在本地核心缓存中存储该值的副本。 - Polity
4个回答

6

查看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/


3
有趣的链接,谢谢。没想到所有的C#写操作都是易失性的!第二个链接回答了我的问题。在锁内存区域中的内存访问不是易失性的,但释放锁会刷新写操作,获取锁会刷新读取缓存,因此值是当前的。 - GazTheDestroyer
这是否涵盖了 .NET 提供的所有类型的锁,包括 Mutex、Monitor、ReaderWriterLock 等等? - Tobias Knauss

1

好的,这篇文章解释了:

  1. 读取操作不能在进入锁之前移动。

  2. 写入操作不能在退出锁之后移动。

同一篇文章中还有更多的解释:

当一个线程退出锁时,第三条规则确保在持有锁期间进行的任何写操作对所有处理器都可见。在另一个线程访问内存之前,读取线程将进入锁定状态,并且第二条规则确保读取操作在获取锁之后逻辑上发生。


1

并非所有的c#内存读写都是易失性的,这不是事实。(想象一下如果是这种情况会对性能造成多大影响!)

但是。

当进入/退出锁时,如何确保刷新来自CPU缓存的这些更新

CPU高速缓存是CPU特定的,但是它们都具有某种形式的 内存一致性协议。也就是说,当您从一个核访问某个内存时,如果另一个核缓存中存在该内存,则CPU使用的协议将确保数据传递到本地核心。

Petar Ivanov在他的回答中所暗示的内容确实非常相关。如果您想更了解他的观点,请查看 内存一致性模型

现在,C#如何保证内存是最新的取决于C#实现者,而Eric Lippert的博客肯定是了解基础问题的好地方。


0

我不确定.NET的情况,但在Java中,明确规定任何以这种方式合作的两个线程必须使用相同的对象进行锁定,才能从您在介绍语句中所说的内容中受益,而不仅仅是任何锁。这是一个关键的区别。

锁定不需要“知道”它保护什么;它只需要确保前一个锁定器编写的所有内容在让其继续之前都可以被另一个锁定器使用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接