【问题标题】:c#: How is Monitor.Wait Implemented?c#: Monitor.Wait 是如何实现的?
【发布时间】:2017-10-17 19:00:48
【问题描述】:

Monitor.Wait() 方法是如何在 C# 的 system.threading.monitor 类内部实现的?

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);
}

【问题讨论】:

  • 据我所知,您问题中链接到的文章有效地回答了它。你在寻找什么额外的信息?
  • 作为一种更好地理解等待函数的方法,我很好奇是否可以使用已发布的 .Net API 将其重新实现为简单函数。它更容易理解为只是代码......
  • 那么我希望你的 C 是好的...... ;) 我会在接下来的一段时间内更新我的答案......

标签: c# multithreading condition-variable


【解决方案1】:

要真正了解是否有更简单的方法来实现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-&gt;Wait() 的调用......在这一点上,我无法进一步追踪......但是。

从这里我们到达Object::Wait (line 531),然后到达SyncBlock::Wait (line 3442)。在这一点上,我们已经掌握了大部分的实现内容,而且还有很多内容。

鉴于以上所有内容并回到您所要求的更简单的实现,我会警惕简化Monitor.Wait()很多在幕后发生,很容易犯错误并在替代实现中出现潜在的错误。

编辑

Scott Chamberlain 大声疾呼,他在 ILSpy 级别下进行了大部分调查并钻研/调试 C 代码堆栈。几乎所有 ILSpy 级别以下的调查工作都是他的,我只是在这里将其编译为答案。

【讨论】:

  • @DiskJunky 我开始发布与您找到的相同的链,但在该方法中的pThis-&gt;Wait(Timeout, exitContext); 调用中碰壁了。
  • 您对 Wait 的猜测不正确,我已经启动并运行了 coreclr 的解决方案,因此我可以点击 Go to Definition。 pThis-&gt;Wait 带你到 Object::Wait,它有一个 GetHeader()-&gt;Wait(timeOut, exitContext) 带你到 ObjHeader::Wait,希望这能帮助你继续追踪事情。
  • 又做了一层,ObjHeader::Wait 有一个pSB-&gt;Wait(timeOut,exitContext) 可以带你到SyncBlock::Wait,这个方法有点吃力,所以我停在那里。
  • 查看代码的最简单方法是克隆 repo,build it 然后为托管或本机代码打开 the visual studio project(您需要运行一次构建以获取 bin\obj\Windows_NT.&lt;Arch&gt;.&lt;BuildType&gt;\CoreCLR.sln 到存在以查看本机代码)。这让您可以使用 Go to Definition 之类的工具来跳转。
猜你喜欢
  • 2012-02-11
  • 1970-01-01
  • 2021-12-10
  • 1970-01-01
  • 2011-03-05
  • 1970-01-01
  • 1970-01-01
  • 2011-04-07
相关资源
最近更新 更多