什么时候.NET Monitor完全进入内核模式?

我想编译一个列表,列出所有可能的条件,使Monitor转到内核模式/使用内核同步对象。

同步块有一个引用内核对象的字段,因此我推断lock将在某个时候进入内核模式。

我发现了这一点: .NET中的Lock(Monitor)内部实现

但它有太多的问题无法回答,唯一有用的信息是OP通过简单地说明lock将在某个时候进入内核模式来回答他自己的问题。 此外,没有任何链接可以支持该答案。

我的问题是不同的 – 我想知道什么时候确切的lock将进入内核模式 (不是,如果不是为什么 – 何时)。

如果与旧版本有任何不同,我更感兴趣的是听到.NET 4和4.5

编辑:从里希特书中:“同步块包含内核对象的字段,拥有线程的ID,递归计数和等待的线程计数。”

通过查看SSCLI20发行版提供的CLR源代码,可以回答大多数这类问题。 它现在已经过时了,它是.NET 2.0的复古版,但很多核心的CLRfunction并没有太大变化。

您要查看的源代码文件是clr / src / vm / syncblk.cpp。 这里有三个类起作用,AwareLock是负责获取锁的低级锁实现,SyncBlock是实现等待进入锁的线程队列的类,CLREvent是操作系统同步的包装器对象,你要问的那个。

这是C ++代码,抽象级别非常高,这段代码与垃圾收集器进行了大量的交互,并且包含了大量的测试代码。 所以我将简要介绍一下这个过程。

SyncBlock具有存储AwareLock实例的m_Monitor成员。 SyncBlock :: Enter()直接调用AwareLock :: Enter()。 它首先试图尽可能便宜地获得锁。 首先检查线程是否已经拥有锁,如果是这种情况,只需增加锁定计数。 接下来使用FastInterlockCompareExchange(),这是一个与Interlocked.CompareExchange()非常相似的内部函数。 如果锁没有争用,那么这很快就会成功,并且Monitor.Enter()返回。 如果没有,那么另一个线程已经拥有锁,使用AwareLock :: EnterEpilog。 需要使用操作系统的线程调度程序,以便使用CLREvent。 必要时动态创建它,并调用其WaitOne()方法。 这将涉及内核转换。

那么就足以回答你的问题了:当争用锁并且线程必须等待时,Monitor类进入内核模式。

当锁具严重争用时。

如果锁定轻微争用,则有一个快速的CPU自旋锁等待锁再次释放,但如果这不等待锁可以释放,则线程将阻塞等待互斥锁,这涉及到内核模式调用暂停线程和其他此类管理。

在spinwait步骤之后。

可能存在额外的智能,例如在单核机器上跳过spinwait,因为有争议的锁只能在释放线程后释放。