C# Monitor.Enter() 源码追踪

start

1[MethodImpl(MethodImplOptions.InternalCall), SecuritySafeCritical, __DynamicallyInvokable] 2public static extern void Enter(object obj);

src/vm/ecall.cpp

1FCFuncStart(gMonitorFuncs) 2 FCFuncElement("Enter", JIT_MonEnter) 3 FCFuncElement("Exit", JIT_MonExit) 4 FCFuncElement("TryEnterTimeout", JIT_MonTryEnter) 5 FCFuncElement("ObjWait", ObjectNative::WaitTimeout) 6 FCFuncElement("ObjPulse", ObjectNative::Pulse) 7 FCFuncElement("ObjPulseAll", ObjectNative::PulseAll) 8 FCFuncElement("ReliableEnter", JIT_MonReliableEnter) 9FCFuncEnd()

next -> JIT_MonEnter

clr/src/vm/jithelpers.cpp

1HCIMPL_MONHELPER (JIT_MonEnterWorker_Portable, Object* obj) 2{ 3 ....... 4}

相关参数 :

1SyncBlock* syncBlock = NULL;//😢同步索引块 2ObjHeader* objHeader = NULL;//对象头 3int spincount = 50;//spin - 轮询 😢采用了轮询机制 4const int MaxSpinCount = 20000 * g_SystemInfo.dwNumberOfProcessors; 5LONG oldvalue, state; 6DWORD tid; 7 8objHeader = obj->GetHeader();//获取请求头 9 10OBJECTREF objRef = ObjectToOBJECTREF(obj);//获取请求头的引用 11 12objRef->EnterObjMonitor();//调用EnterObjMonitor

源码追踪

1.GetHeader()方法获取对象头ObjHeader,在ObjHeader里有对EnterObjMonitor()方法的定义:

clr/src/vm/object.cpp

1// 获取请求头 2// 访问对象上负偏移量的ObjHeader(因为高速缓存线路) 3// Access the ObjHeader which is at a negative offset on the object (because of 4// cache lines) 5ObjHeader *GetHeader() 6{ 7 LEAF_CONTRACT; 8 return PTR_ObjHeader(PTR_HOST_TO_TADDR(this) - sizeof(ObjHeader)); 9}

next->查看ObjHeader的EnterObjMonitor的定义

clr/src/vm/syncblk.cpp

1void ObjHeader::EnterObjMonitor() 2{ 3 WRAPPER_CONTRACT; 4 GetSyncBlock()->EnterMonitor(); 5}

又调用了GetSyncBlock的EnterMonitor 追下去。

clr/src/vm/syncblk.h

1void EnterMonitor() 2{ 3 WRAPPER_CONTRACT; 4 m_Monitor.Enter(); 5}

调用了m_Monitor的Enter

查看m_Monitor的定义:

1protected: 2AwareLock m_Monitor; // the actual monitor

继续 查看AwareLock的Enter方法定义,感觉已经越来越近了😢

1void AwareLock::Enter() 2{ 3 CONTRACTL 4 { 5 INSTANCE_CHECK; 6 THROWS; 7 GC_TRIGGERS; 8 MODE_ANY; 9 INJECT_FAULT(COMPlusThrowOM();); 10 } 11 CONTRACTL_END; 12 13 Thread *pCurThread = GetThread(); 14 15 for (;;) 16 { 17 // Read existing lock state. 18 volatile LONG state = m_MonitorHeld; 19 20 if (state == 0) 21 { 22 // Common case: lock not held, no waiters. Attempt to acquire lock by 23 //常见情况:锁没锁,没有服务员。试图获得锁定 24 // switching lock bit. 25 //开关锁。 26 if (FastInterlockCompareExchange((LONG*)&m_MonitorHeld, 1, 0) == 0)//cas 修改值。 27 { 28 break; 29 } 30 } 31 else 32 { 33 // It's possible to get here with waiters but no lock held, but in this 34 // case a signal is about to be fired which will wake up a waiter. So 35 // for fairness sake we should wait too. 36 // Check first for recursive lock attempts on the same thread. 37 if (m_HoldingThread == pCurThread)//如果为当前线程 38 { 39 goto Recursion; 40 } 41 42 // Attempt to increment this count of waiters then goto contention 43 // handling code. 44 if (FastInterlockCompareExchange((LONG*)&m_MonitorHeld, (state + 2), state) == state) 45 { 46 goto MustWait; 47 } 48 } 49 50 } 51 52 // We get here if we successfully acquired the mutex. 53 m_HoldingThread = pCurThread; 54 m_Recursion = 1; 55 pCurThread->IncLockCount(); 56 57#if defined(_DEBUG) && defined(TRACK_SYNC) 58 { 59 // The best place to grab this is from the ECall frame 60 Frame *pFrame = pCurThread->GetFrame(); 61 int caller = (pFrame && pFrame != FRAME_TOP 62 ? (int) pFrame->GetReturnAddress() 63 : -1); 64 pCurThread->m_pTrackSync->EnterSync(caller, this); 65 } 66#endif 67 68 return; 69 70MustWait: 71 // Didn't manage to get the mutex, must wait. //继续等待 72 EnterEpilog(pCurThread); 73 return; 74 75Recursion: 76 // Got the mutex via recursive locking on the same thread. 77 _ASSERTE(m_Recursion >= 1); 78 m_Recursion++;//递归次数加1 79#if defined(_DEBUG) && defined(TRACK_SYNC) 80 // The best place to grab this is from the ECall frame 81 Frame *pFrame = pCurThread->GetFrame(); 82 int caller = (pFrame && pFrame != FRAME_TOP ? (int) pFrame->GetReturnAddress() : -1); 83 pCurThread->m_pTrackSync->EnterSync(caller, this); 84#endif 85}

从上面的代码我们可以看到,先使用GetThread()获取当前的线程,然后取出m_MonitorHeld字段,如果现在没有线程进入临界区,则设置该字段的状态,然后将m_HoldingThread设置为当前线程,从这一点上来这与Win32的过程应该是一样的。

如果从m_MonitorHeld字段看,有线程已经进入临界区则分两种情况:第一,是否已进入的线程如当前线程是同一个线程,如果是,则把m_Recursion递加,如果不是,则通过EnterEpilog( pCurThread)方法,当前线程进入线程等待队列。

通过上面的文字描述和代码的跟踪,在我们的大脑中应该有这样一张图了:

转载


confirm

clr/src/vm/syncblk.h

ObjHeader has an index to a SyncBlock. This index is 0 for the bulk of all

ObjHeader有一个指向同步块的索引。大多数情况下,这个指数是0


相关链接

https://github.com/SSCLI/sscli20_20060311

http://www.aspphp.online/bianchen/dnet/gydnet/201701/14624.html


此次只是简单的追踪了一番,部分内容可能不详细。

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

2020年前端实用代码段,为你的工作保驾护航

有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )