.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 中的锁定(监视器)内部实现的主要内容,如果未能解决你的问题,请参考以下文章