<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12
      代碼改變世界

      揭示同步塊索引(上):從lock開始

      2009-03-13 12:27  橫刀天笑  閱讀(9967)  評論(21)    收藏  舉報

      大家都知道引用類型對象除實例字段的開銷外,還有兩個字段的開銷:類型指針和同步塊索引(SyncBlockIndex)。同步塊索引這個東西比起它的兄弟類型指針更少受人關注,顯得有點冷落,其實此兄功力非凡,在CLR里可謂叱咤風云,很多功能都要借助它來實現。 接下來我會用三篇來介紹同步塊索引在.NET中的所作所為。
      既然本章副標題是從lock開始,那我就舉幾個lock的示例:

      代碼1

         1: public class Singleton
         2: {
         3:     private static object lockHelper = new object();
         4:     private static Singleton _instance = null;
         5:     public static Singleton Instance
         6:     {
         7:         get
         8:         {
         9:             lock(lockHelper)
        10:             {
        11:                 if(_instance == null)
        12:                     _instance = new Singleton();
        13:             }
        14:             return _instance;
        15:         }
        16:     }
        17: } 

      代碼2

         1: public class Singleton
         2: {
         3:     private static Singleton _instance = null;
         4:     public static Singleton Instance
         5:     {
         6:         get
         7:         {
         8:             object lockHelper = new object();
         9:             lock(lockHelper)
        10:             {
        11:                 if(_instance==null)
        12:                     _instance = new Singleton();
        13:             }
        14:             return _instance;
        15:         }
        16:     }
        17: } 

      代碼3

         1: public class Singleton
         2: {
         3:     private static Singleton _instance = null;
         4:     public static Singleton Instance
         5:     {
         6:         get
         7:         {
         8:             lock(typeof(Singleton))
         9:             {
        10:                 if(_instance==null)
        11:                     _instance = new Singleton();
        12:             }
        13:             return_instance;
        14:         }
        15:     }
        16: } 

      代碼4

         1: public void DoSomething()
         2: {
         3:     lock(this)
         4:     {
         5:         //dosomething
         6:     }
         7: } 

      上面四種代碼,對于加鎖的方式來說(不討論其他)哪一種是上上選?對于這個問題的答案留在本文最后解答。

      讓我們先來看看在Win32的時代,我們如何做到CLR中的lock的效果。在Win32時,Windows為我們提供了一個CRITICAL_SECTION結構,看看上面的單件模式,如果使用CRITICAL_SECTION的方式如何實現?

         1: class Singleton
         2: {
         3:     private:
         4:         CRITICAL_SECTIONg_cs;
         5:         static Singleton _instance = NULL;
         6:     public:
         7:         Singleton()
         8:         {
         9:             InitializeCriticalSection(&g_cs);
        10:         }
        11:         static Singleton GetInstance()
        12:         {
        13:             EnterCriticalSection(&g_cs);
        14:             if(_instance!=NULL)
        15:                 _instance=newSingleton();
        16:             LeaveCriticalSection(&g_cs);
        17:             return_instance;
        18:         }
        19:         ~Singleton()
        20:         {
        21:             DeleteCriticalSection(&g_cs);
        22:         }
        23: }

      Windows提供四個方法來操作這個CRITICAL_SECTION,在構造函數里我們使用InitializeCriticalSection這個方法初始化這個結構,它知道如何初始化CRITICAL_SECTION結構的成員,當我們要進入一個臨界區訪問共享資源時,我們使用EnterCriticalSection方法,該方法首先會檢查CRITICAL_SECTION的成員,檢查是否已經有線程進入了臨界區,如果有,則線程會等待,否則會設置CRITICAL_SECTION的成員,標識出本線程進入了臨界區。當臨界區操作結束后,我們使用LeaveCriticalSection方法標識線程離開臨界區。在Singleton類的析構函數里,我們使用DeleteCriticalSection方法銷毀這個結構。整個過程就是如此。
      我們可以在WinBase.h里找到CRITICAL_SECTION的定義:

      typedef RTL_CRITICAL_SECTION CRITICAL_SECTION;

        可以看到,CRITICAL_SECTION實際上就是RTL_CRITICAL_SECTION,而RTL_CRITICAL_SECTION又是在WinNT.h里定義的:

         1: typedef struct _RTL_CRITICAL_SECTION{
         2: PRTL_CRITICAL_SECTION_DEBUGDebugInfo;
         3: //
         4: //Thefollowingthreefieldscontrolenteringandexitingthecritical
         5: //sectionfortheresource
         6: //
         7: LONG LockCount;
         8: LONG RecursionCount;
         9: HANDLE OwningThread;//fromthethread'sClientId->UniqueThread
        10: HANDLE LockSemaphore;
        11: ULONG _PTRSpinCount;//forcesizeon64-bitsystemswhenpacked
        12: }RTL_CRITICAL_SECTION,*PRTL_CRITICAL_SECTION; 

         從上面的定義和注釋,聰明的你肯定知道Windows API提供的這幾個方法是如何操作CRITICAL_SECTION結構的吧。在這里我們只需要關注OwningThread成員,當有線程進入臨界區的時候,這個成員就會指向當前線程的句柄。

      說了這么多,也許有人已經厭煩了,不是說好說lock么,怎么說半天Win32 API呢,實際上CLR的lock與Win32 API實現方式幾乎是一樣的。但CLR并沒有提供CRITICAL_SECTION結構,不過CLR提供了同步塊,CLR還提供了System.Threading.Monitor類。

      實際上使用lock的方式,與下面的代碼是等價的:

         1: try{ 
         2:     Monitor.Enter(obj); 
         3:     //… 
         4: }finally{ 
         5:     Monitor.Exit(obj); 
         6: } 

      (以下內容只限制在本文,為了簡單,有的說法很片面,更詳細的內容會在后面兩篇里描述)

      當CLR初始化的時候,CLR會初始化一個SyncBlock的數組,當一個線程到達Monitor.Enter方法時,該線程會檢查該方法接受的參數的同步塊索引,默認情況下對象的同步塊索引是一個負數(實際上并不是負數,我這里只是為了敘說方便),那么表明該對象并沒有一個關聯的同步塊,CLR就會在全局的SyncBlock數組里找到一個空閑的項,然后將數組的索引賦值給該對象的同步塊索引,SyncBlock的內容和CRITICAL_SECTION的內容很相似,當Monitor.Enter執行時,它會設置SyncBlock里的內容,標識出已經有一個線程占用了,當另外一個線程進入時,它就會檢查SyncBlock的內容,發現已經有一個線程占用了,該線程就會等待,當Monitor.Exit執行時,占用的線程就會釋放SyncBlock,其他的線程可以進入操作了。

      好了,有了上面的解釋,我們現在可以判斷本文前面給出的幾個代碼,哪一個是上上選呢?

      對于代碼2,鎖定的對象是作為一個局部變量,每個線程進入的時候,鎖定的對象都會不一樣,它的SyncBlock每一次都是重新分配的,這個根本談不上什么鎖定不鎖定。

      對于代碼3,一般說來應該沒有什么事情,但這個操作卻是很危險的,typeof(Singleton)得到的是Singleton的Type對象,所有Singleton實例的Type都是同一個,Type對象也是一個對象,它也有自己的SyncBlock,Singleton的Type對象的SyncBlock在程序中只會有一份,為什么說這種做法是危險的呢?如果在該程序中,其他毫不相干的地方我們也使用了lock(typeof(Singleton)),雖然它和這里的鎖定毫無關系,但是只要一個地方鎖定了,各個地方的線程都會在等待。

      對于代碼4,實際上代碼4的性質和代碼3差不多,如果有一個地方使用了DoSomething方法所在類的實例進行lock,而且恰好如this是同一個實例,那么兩個地方就會互斥了。

      由此看來只有代碼1是上上選,之所以是這樣,是因為代碼1將鎖定的對象作為私有字段,只有這個對象內部可以訪問,外部無法鎖定。 上面只是從文字上敘說,也許你覺得證據不足,我們就搬來代碼作證。 使用ILDasm反編譯上面單件模式的Instance屬性的代碼,其中一段IL代碼如下所示:

         1: IL_0007:stloc.1
         2: IL_0008:call void [mscorlib]System.Threading.Monitor::Enter(object)
         3: IL_000d:nop
         4: .try
         5: {
         6:     IL_000e:nop
         7:     IL_000f:ldsfld class Singleton Singleton::_instance
         8:     //….
         9:     //…
        10: }
        11: finally
        12: {
        13:     IL_002b:ldloc.1
        14:     IL_002c:call void [mscorlib]System.Threading.Monitor::Exit(object)
        15:     IL_0031:nop
        16:     IL_0032:endfinally
        17: } 

      為了簡單,我省去了一部分代碼。但是很明顯,我們看到了System.Threading.Monitor.Enter和Exit。然后我們拿出Reflector看看這個Monitor到底是何方神圣。哎呀,發現Monitor.Enter和Monitor.Exit的代碼如下所示:

         1: [MethodImpl(MethodImplOptions.InternalCall)]
         2: public static extern void Enter(objectobj);
         3: [MethodImpl(MethodImplOptions.InternalCall),ReliabilityContract(Consistency.WillNotCorruptState,Cer.Success)]
         4: public static extern void Exit(objectobj); 

      只見方法使用了extern關鍵字,方法上面還標有[MethodImpl(MethodImplOptions.InternalCall)]這樣的特性,實際上這說明Enter和Exit的代碼是在內部C++的代碼實現的。只好拿出Rotor的代碼求助了,對于所有"內部實現"的代碼,我們可以在sscli20\clr\src\vm\ecall.cpp里找到映射:

         1: FCFuncStart(gMonitorFuncs) 
         2: FCFuncElement("Enter", JIT_MonEnter) 
         3: FCFuncElement("Exit", JIT_MonExit) 
         4:
         5: FCFuncEnd() 

      原來Enter映射到JIT_MonEnter,一步步的找過去,我們最終到了這里:

      Sscli20\clr\src\vm\jithelpers.cpp:

         1: HCIMPL_MONHELPER(JIT_MonEnterWorker_Portable, Object* obj) 
         2: { 
         3:     //省略大部分代碼 
         4:     OBJECTREF objRef = ObjectToOBJECTREF(obj); 
         5:     objRef->EnterObjMonitor(); 
         6: } 
         7: HCIMPLEND 

      objRef就是object的引用,EnterObjMonitor方法的代碼如下:

         1: void EnterObjMonitor() 
         2: { 
         3:     GetHeader()->EnterObjMonitor(); 
         4: } 

      GetHeader()方法獲取對象頭ObjHeader,在ObjHeader里有對EnterObjMonitor()方法的定義:

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

      GetSyncBlock()方法會獲取該對象對應的SyncBlock,在SyncBlock里有EnterMonitor方法的定義:

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

      離核心越來越近了,m_Monitor是一個AwareLock類型的字段,看看AwareLock類內Enter方法的定義:

         1: void AwareLock::Enter() 
         2: { 
         3:     Thread* pCurThread = GetThread(); 
         4:     for (;;) 
         5:     { 
         6:          volatile LONG state = m_MonitorHeld; 
         7:         if (state == 0) 
         8:         { 
         9:             // Common case: lock not held, no waiters. Attempt to acquire lock by 
        10:              // switching lock bit. 
        11:             if (FastInterlockCompareExchange((LONG*)&m_MonitorHeld, 1, 0) == 0) 
        12:             { 
        13:                 break; 
        14:             } 
        15:         } 
        16:         else 
        17:         { 
        18:             // It's possible to get here with waiters but no lock held, but in this 
        19:              // case a signal is about to be fired which will wake up a waiter. So 
        20:              // for fairness sake we should wait too. 
        21:              // Check first for recursive lock attempts on the same thread. 
        22:              if (m_HoldingThread == pCurThread) 
        23:              { 
        24:                  goto Recursion; 
        25:              } 
        26:             // Attempt to increment this count of waiters then goto contention 
        27:             // handling code. 
        28:         if (FastInterlockCompareExchange((LONG*)&m_MonitorHeld, (state + 2), state) == state) 
        29:         { 
        30:              goto MustWait;  
        31:         } 
        32:     } 
        33: } 
        34:     // We get here if we successfully acquired the mutex. 
        35:     m_HoldingThread = pCurThread; 
        36:     m_Recursion = 1; 
        37:     pCurThread->IncLockCount(); 
        38:     return; 
        39: MustWait: 
        40:      // Didn't manage to get the mutex, must wait. 
        41:     EnterEpilog(pCurThread); 
        42:      return; 
        43:     Recursion: 
        44:      // Got the mutex via recursive locking on the same thread. 
        45:     m_Recursion++; 
        46: } 

      從上面的代碼我們可以看到,先使用GetThread()獲取當前的線程,然后取出m_MonitorHeld字段,如果現在沒有線程進入臨界區,則設置該字段的狀態,然后將m_HoldingThread設置為當前線程,從這一點上來這與Win32的過程應該是一樣的。如果從m_MonitorHeld字段看,有線程已經進入臨界區則分兩種情況:第一,是否已進入的線程如當前線程是同一個線程,如果是,則把m_Recursion遞加,如果不是,則通過EnterEpilog(pCurThread)方法,當前線程進入線程等待隊列。

      通過上面的文字描述和代碼的跟蹤,在我們的大腦中應該有這樣一張圖了:

      總結

      現在你應該知道lock背后發生的事情了吧。下一次面試的時候,當別人問你同步塊索引的時候,你就可以滔滔不絕的和他論述一番。接下來還有兩篇分析同步塊的其他作用。
      歡迎拍磚,祝編程愉快。

      主站蜘蛛池模板: 少妇伦子伦精品无吗| 人妻少妇精品视频三区二区| 亚洲夂夂婷婷色拍WW47| 日韩 高清 无码 人妻| 视频一区二区三区自拍偷拍| 日本深夜福利在线观看| 人妻 日韩精品 中文字幕| 成人精品动漫一区二区| 深夜av免费在线观看| 福贡县| 精品久久亚洲中文无码| 大陆精大陆国产国语精品| 40岁成熟女人牲交片20分钟| 国产精品蜜臀av在线一区| 无码人妻aⅴ一区二区三区蜜桃 | 亚洲精品一区二区妖精| 精品国产三级a∨在线欧美| 日韩午夜福利片段在线观看 | 国产微拍一区二区三区四区| 日韩中文字幕综合第二页| 国产一区二区视频啪啪视频| 欧美高清狂热视频60一70| 国产深夜福利在线免费观看 | 免费人成再在线观看网站| 日本高清一区二区三| 热久久这里只有精品国产| 精品国产人妻一区二区三区久久| 龙江县| 精品国产综合成人亚洲区| 国产一级二级三级毛片| 色综合天天综合天天更新| 中文人妻AV大区中文不卡| 南汇区| 高潮迭起av乳颜射后入| 久久国产精品精品国产色| 国产午夜福利在线视频| 日本亚洲色大成网站www久久| 国产精品成人一区二区三区| 香港日本三级亚洲三级| 国产精品呻吟一区二区三区| 女人与牲口性恔配视频免费|