.NET 中的锁定(监视器)内部实现

Posted

技术标签:

【中文标题】.NET 中的锁定(监视器)内部实现【英文标题】:Lock (Monitor) internal implementation in .NET 【发布时间】:2011-07-03 23:08:01 【问题描述】:

要掌握某些技术,您必须知道它是如何在更低的抽象级别上实现的。在多线程编程的情况下,最好了解同步原语。 这是一个问题,如何在 .NET 中实现 Lock (Monitor)?

我对这些方面很感兴趣: - 它是否使用操作系统对象?; - 它需要用户模式还是内核模式?; - 等待锁定的线程的开销是多少?; - 在什么情况下,等待锁的线程队列可能会被违反?

更新: “如果多个线程争用锁,它们将在“就绪队列”中排队,并按照先到先得的原则授予锁。注意:Windows 和 CLR 行为中的细微差别意味着队列的公平性有时会被违反。”[C# 4.0 in a Nutshell, Joseph Albahari] 所以这就是我在最后一个关于“违反队列”的问题中要问的问题。

【问题讨论】:

不,用户,无论获取锁需要多长时间,都不知道“违规队列”是什么样的。试着提出一个更好的问题。 如果我没记错的话,它会尝试旋转一段时间,如果这不起作用,它会返回内核。因此,如果锁不被争用,它会相当便宜,但对于高锁争用,它可能会变得更加昂贵。 此行为看起来与 Windows 中的关键部分相同。这是否意味着关键部分在后台使用? 【参考方案1】:

Wikipedia article 很好地描述了“监视器”是什么,以及它的底层技术条件变量。

请注意,.NET Monitor 是条件变量的正确实现;大多数已发布的 CV 的 Win32 实现都是不正确的,即使是在通常有信誉的来源(如 Dobbs 博士)中找到的也是如此。这是因为简历cannot easily be built from the existing Win32 synchronization primitives。

.NET CV 实现不是仅仅在 Win32 原语上构建一个浅层(且不正确)的包装器,而是利用它位于 .NET 平台上的事实,实现自己的等待队列等。

【讨论】:

在 .NET 4.0 中它更加正确。如果在Monitor.Enter 之后出现异常,则有可能使锁保持“卡住”(参见***.com/questions/2837070/…) 如果您想在 Win32 中使用条件变量,请使用随 Windows V6 (Vista/2008) 引入的操作系统实现:msdn.microsoft.com/en-gb/library/ms682052(VS.85).aspx @Richard:感谢您的提示!我不知道他们在 Win32 级别引入了这个。 你的***链接没有回答任何问题,问题的最重要部分是内核与用户模式......那篇文章是另一个聪明的聪明点【参考方案2】:

经过一些调查,我找到了问题的答案。一般来说,CodeInChaos 和 Henk Holterman 是对的,但这里有一些细节。

当线程首先开始与其他线程竞争锁时,它会旋转等待循环一段时间以尝试获取锁。所有这些操作都在用户模式中执行。然后如果没有成功的操作系统内核对象Event 创建,线程切换到内核模式 并等待来自这个Event 的信号。

所以回答我的问题是: 1. 在更好的情况下不,但在更糟糕的情况下是(Event 对象在需要时懒惰地创建); 2. 一般在用户模式下工作,但如果线程竞争锁的时间过长,线程可以切换到内核模式(通过Win API非托管函数调用); 3. 从用户模式切换到内核模式的开销(约 1000 个 CPU 周期); 4. 微软声称它是像 FIFO 一样的“诚实”算法,但它不保证这一点。 (例如,如果来自“等待队列”的线程将被挂起,它将在恢复时移动到队列的末尾。)

【讨论】:

您能发布任何研究资料吗?那会很有帮助 我想看看1和2语句的详细解释。我想知道 .NET 何时决定在 Monitor 实现中进入内核模式 这在 Clr Via C# 中有详细讨论,Jeffrey Richter 称其为“混合锁”。在用户模式下,监视器旋转并与其他线程竞争(因为它们没有被阻塞),一旦它们被提升为内核模式锁,它们就会被阻塞,从而释放周期。

以上是关于.NET 中的锁定(监视器)内部实现的主要内容,如果未能解决你的问题,请参考以下文章

总线锁定与一致性缓存

锁优化

java并发之synchronized解密

java使用反射实现动态代理时,抛出UndeclaredThrowableException异常

Synchronized理解及用法

Java NIO 选择器(Selector)的内部实现(poll epoll)