大多数这类问题都可以通过查看SSCLI20 发行版中提供的 CLR 源代码来回答。它现在已经过时了。它是 .NET 2.0 的经典版本,但许多核心 CLR 功能并没有太大变化。
您要查看的源代码文件是 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 类进入内核模式。