要真正了解是否有更简单的方法来实现Monitor.Wait,我们将深入研究它是如何在低级别运行的。实际实现最终是用 C 语言编写的,对我们隐藏,但专门针对 Monitor.Wait(object),我们可以通过以下方式跟踪调用链;
Monitor.Wait(o)
-- return Monitor.Wait(o, -1, false)
Monitor.Wait(o, -1, false)
-- Monitor.ObjWait(false [exitContext], -1 [millisecondsTimeout], o)
即使在 ILSpy 中,从这里也很难看到发生了什么。根据 Tigran 指向 Monitor 对象源的链接,我们在源中留下以下内容;
/*========================================================================
** Waits for notification from the object (via a Pulse/PulseAll).
** timeout indicates how long to wait before the method returns.
** This method acquires the monitor waithandle for the object
** If this thread holds the monitor lock for the object, it releases it.
** On exit from the method, it obtains the monitor lock back.
** If exitContext is true then the synchronization domain for the context
** (if in a synchronized context) is exited before the wait and reacquired
**
** Exceptions: ArgumentNullException if object is null.
========================================================================*/
[System.Security.SecurityCritical] // auto-generated
[ResourceExposure(ResourceScope.None)]
[MethodImplAttribute(MethodImplOptions.InternalCall)]
private static extern bool ObjWait(bool exitContext, int millisecondsTimeout, Object obj)
对于它正在做什么以及以什么顺序进行的描述是不言自明的。然而,它的精确实现是由包含关键代码的各种 private static extern 方法包装而成的。
extern specifies 实际实现位于另一个程序集中。在访问非托管代码时,它可以与DllImport 一起使用(这里不是这种情况),也可以是extern alias。根据 SO 帖子asking about where to find the implementation of extern methods 从这里开始,您必须查看可以在Core CLR 中找到的 C 代码本身(信用 Scott Chamberlain)。
从这里我们正在查看 CLR 中 ObjWait() 的 C 方法实现,其中 maps(第 1027 行)到 ObjectNative::WaitTimeout;
FCIMPL3(FC_BOOL_RET, ObjectNative::WaitTimeout, CLR_BOOL exitContext, INT32 Timeout, Object* pThisUNSAFE)
{
FCALL_CONTRACT;
BOOL retVal = FALSE;
OBJECTREF pThis = (OBJECTREF) pThisUNSAFE;
HELPER_METHOD_FRAME_BEGIN_RET_1(pThis);
if (pThis == NULL)
COMPlusThrow(kNullReferenceException, W("NullReference_This"));
if ((Timeout < 0) && (Timeout != INFINITE_TIMEOUT))
COMPlusThrowArgumentOutOfRange(W("millisecondsTimeout"), W("ArgumentOutOfRange_NeedNonNegNum"));
retVal = pThis->Wait(Timeout, exitContext);
HELPER_METHOD_FRAME_END();
FC_RETURN_BOOL(retVal);
}
FCIMPLEND
在开始讨论之前,值得一看 this(也归功于 Scott Chamberlain),其中指出;
FCall 在托管代码中被标识为设置了 MethodImplOptions.InternalCall 位的外部方法。
这解释了我们与ObjWait() 和ObjectNative::WaitTimeout 的链接。因此,进一步分解这一点,我们可以看到基本的null 和参数检查,如果是这样,则会引发适当的异常。关键是对pThis->Wait() 的调用......在这一点上,我无法进一步追踪......但是。
从这里我们到达Object::Wait (line 531),然后到达SyncBlock::Wait (line 3442)。在这一点上,我们已经掌握了大部分的实现内容,而且还有很多内容。
鉴于以上所有内容并回到您所要求的更简单的实现,我会警惕简化Monitor.Wait()。 很多在幕后发生,很容易犯错误并在替代实现中出现潜在的错误。
编辑
向 Scott Chamberlain 大声疾呼,他在 ILSpy 级别下进行了大部分调查并钻研/调试 C 代码堆栈。几乎所有 ILSpy 级别以下的调查工作都是他的,我只是在这里将其编译为答案。