.NET Monitor 何时进入内核模式?

9
我想编制一个所有可能导致监视器进入内核模式或使用内核同步对象的条件列表。
同步块有一个字段用于引用内核对象。因此,我推断 lock 有时会进入内核模式。
我找到了这个链接:Lock (Monitor) internal implementation in .NET,但是它有太多问题需要回答,唯一有用的信息是OP通过简单地说明 lock 有时会进入内核模式来回答自己的问题。此外,没有任何链接支持这个答案。 lock 什么时候会进入内核模式(不是是否和为什么 - 而是何时)?
如果有.NET 4和4.5的区别,我更感兴趣。
根据 Richter 的书:"同步块包含内核对象、拥有线程的ID、递归计数和等待线程计数的字段。"

停止回滚有建设性的编辑。编辑是该网站的核心部分;请参阅https://stackoverflow.com/help/editing。 - Zoe stands with Ukraine
3个回答

23
大多数此类问题可以通过查看SSCLI20 distribution提供的CLR源代码来回答。现在这个版本已经相当陈旧了,它是.NET 2.0的版本,但很多核心CLR特性并没有改变太多。
您需要查看的源代码文件是clr/src/vm/syncblk.cpp。这里有三个类起作用。AwareLock是低级锁实现,负责获取锁定;SyncBlock是实现等待进入锁定的线程队列的类;CLREvent是操作系统同步对象的包装器,就是您所询问的那个对象。
这是C++代码,抽象级别相当高。这段代码与垃圾收集器密切交互,并包含大量测试代码。因此,我将简要描述一下这个过程。
SyncBlock有一个m_Monitor成员,它存储AwareLock实例。SyncBlock :: Enter()直接调用AwareLock :: Enter()。它首先尝试尽可能廉价地获取锁定。首先检查线程是否已经拥有锁定,如果是,则只增加锁定计数。接下来使用FastInterlockCompareExchange(),这是一个类似于Interlocked.CompareExchange()的内部函数。如果锁未受争夺,则此操作非常快,并且Monitor.Enter()返回。如果没有,则另一个线程已经拥有锁定,并使用AwareLock :: EnterEpilog。需要让操作系统的线程调度程序参与,因此使用CLREvent。如果必要,动态创建它并调用其WaitOne()方法。这将涉及内核转换。

因此,有足够的内容回答您的问题:当锁定受到争用并且线程必须等待时,Monitor类会进入内核模式。


非常感谢您,特别是对于SSCLI - 我不知道这是公开可用的。 - Boppity Bop
4
谢谢您的评论,指引我找到了CLR源代码中相关的部分。我特别关注旋转部分:一般认为Monitor在进入内核之前会先进行旋转。我对它的具体旋转方式感兴趣(迭代次数等)。我在您描述的代码路径中没有看到旋转,但我在被AwareLock :: TryEnter调用的AwareLock :: Contention中看到了旋转逻辑。现在似乎只有在使用timeout时,TryEnter才适用这种旋转逻辑,所以我猜想在使用C#lock关键字时不会使用旋转。我的理解正确吗? - Martin Pozor

4

当锁被严重争用时。

如果锁轻度争用,会有一个快速的CPU自旋锁等待锁再次空闲,但是如果等待时间不够长,无法获得锁,线程将会进入阻塞状态等待互斥锁,这会涉及到内核模式调用来暂停线程和其他相关管理操作。


0

在自旋等待步骤之后,可能存在额外的智能,例如在单核机器上跳过自旋等待,因为争用的锁只有在释放线程之后才能被释放。


安德鲁,我需要将所有步骤呈现在平面视图中,以便查看它们如何回答问题。你的回答就像是 - 当人们被闪电击中死亡时 - 他们已经死了。 - Boppity Bop
@BoppityBop:先去弄一架飞机和一个飞行跑道,然后我们再讨论这个问题。 - Lightness Races in Orbit
如果你想要更精确的时间,那么“在5:04:02.008”怎么样?但是,绝对的答案是没有用的,而相对的答案则会有所不同。唯一剩下的就是一个逻辑上的答案。在完成前面的步骤之后,你的问题缺少了一个你想要的重点。你为什么关心这个问题呢?如果你提供了这个信息,我们或许能够提供一个更有用的答案。 - Andrew Arnott

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