[英]c#: How is Monitor.Wait Implemented?
如何在C#的system.threading.monitor类内部实现Monitor.Wait()方法?
https://www.codeproject.com/Articles/28785/Thread-synchronization-Wait-and-Pulse-demystified
从概念上讲,我正在设想这样的事情:
class Monitor {
public static Wait(object o)
{
// Release Lock
Monitor.Exit(o);
// Spinlock until another Thread acquires Lock
while(!Monitor.isEnter(o));
// Wait to re-acquire lock
Monitor.Enter(o);
}
}
这样准确吗? 还是我想念什么?
这是我考虑作为前面代码基础的典型监视器类示例。
using System.Threading;
readonly object o = new object();
// In Thread #1: (Where appropriate)
lock(o) {
Monitor.Wait(o);
}
//In Thread #2: (Where appropriate)
lock(o) {
Monitor.Pulse(o);
}
Lock(o)当然是内置的c#快捷方式,用于:
try {
Monitor.Enter(o);
{
//Lock Block Here
}
}
finally {
Monitor.Exit(o);
}
要真正了解是否有实现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
指定实际的实现位于另一个程序集中。 访问非托管代码时,它可以与DllImport
一起使用(此处不是这种情况),也可以是extern别名 。 根据SO帖子,在这里询问在哪里可以找到extern方法的实现 ,您必须查看可以在Core CLR (信用Scott Chamberlain)中找到的C代码本身。
从这里开始,我们观察ObjWait()
的C方法实现,该方法将 (第1027行) 映射到CLR中的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
进入此之前,它的价值看这个 (也归功于斯科特·张伯伦),其中规定;
FCall在托管代码中通过设置MethodImplOptions.InternalCall位设置为外部方法。
这解释了我们到ObjWait()
和ObjectNative::WaitTimeout
。 因此,进一步分解,我们可以看到基本的null
和参数检查,并在适当的情况下引发了异常。 症结在于对pThis->Wait()
的调用……在这一点上,我还无法进一步跟踪……。
从这里我们到达Object::Wait
( 第531行 ),然后转到SyncBlock::Wait
( 第3442行 )。 至此,我们已经掌握了实现的大部分内容,并且还有很多。
鉴于以上所有内容,然后返回您要求的是一个更简单的实现,我对简化Monitor.Wait()
持谨慎Monitor.Wait()
。 幕后发生了很多事情,很容易犯一个错误,并且在替代实现中有潜在的错误。
编辑
认真地向斯科特·张伯伦大喊大叫, 斯科特·张伯伦在ILSpy级别以下进行了大部分调查,并对C代码堆栈进行了研究/调试。 ILSpy级别以下的几乎所有调查工作都是他的,这里我只是将其整理成一个答案。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.