日韩成人免费在线_国产成人一二_精品国产免费人成电影在线观..._日本一区二区三区久久久久久久久不

當前位置:首頁 > 科技  > 軟件

讓我來告訴你,C# 線程本地存儲為什么線程間值不一樣

來源: 責編: 時間:2024-01-26 09:04:04 278觀看
導讀一:背景1. 講故事有朋友在微信里面問我,為什么用 ThreadStatic 標記的字段,只有第一個線程拿到了初始值,其他線程都是默認值,讓我能不能幫他解答一下,尼瑪,我也不是神仙什么都懂,既然問了,那我試著幫他解答一下,也給后面類似疑

一:背景

1. 講故事

有朋友在微信里面問我,為什么用 ThreadStatic 標記的字段,只有第一個線程拿到了初始值,其他線程都是默認值,讓我能不能幫他解答一下,尼瑪,我也不是神仙什么都懂,既然問了,那我試著幫他解答一下,也給后面類似疑問的朋友解個惑吧。KoG28資訊網——每日最新資訊28at.com

二:為什么值不一樣

1. 問題復現

為了方便講述,定義一個 ThreadStatic 的變量,然后用多個線程去訪問,參考代碼如下:KoG28資訊網——每日最新資訊28at.com

internal class Program{    [ThreadStatic]    public static int num = 10;    static void Main(string[] args)    {        Test();        Console.ReadLine();    }    /// <summary>    /// 1. 特性方式    /// </summary>    static void Test()    {        var t1 = new Thread(() =>        {            Debugger.Break();            var j = num;            Console.WriteLine($"tid={Thread.CurrentThread.ManagedThreadId}, num={j}");        });        t1.Start();        t1.Join();        var t2 = new Thread(() =>        {            Debugger.Break();            var j = num;            Console.WriteLine($"tid={Thread.CurrentThread.ManagedThreadId}, num={j}");        });        t2.Start();    }}

圖片圖片KoG28資訊網——每日最新資訊28at.com

從代碼中可以看到,確實如朋友所說,一個是num=10,一個是num=0 ,那為什么會出現這樣的情況呢?KoG28資訊網——每日最新資訊28at.com

2. 從匯編上尋找答案

作為C#程序員,真的需要掌握一點匯編,往往就能找到問題的突破口,先看一下thread1 中的 var j = num;所對應的匯編代碼,參考如下:KoG28資訊網——每日最新資訊28at.com

D:/code/MyApplication/ConsoleApp7/Program.cs @ 27:08893737 b9a0dd6808      mov     ecx,868DDA0h0889373c ba04000000      mov     edx,408893741 e84a234e71      call    coreclr!JIT_GetSharedNonGCThreadStaticBase (79d75a90)08893746 8b4814          mov     ecx,dword ptr [eax+14h]08893749 894df8          mov     dword ptr [ebp-8],ecx

從匯編上可以看到,這個 num=10 是來自于 eax+14h 的地址上,而 eax 是 JIT_GetSharedNonGCThreadStaticBase 函數的返回值,言外之意核心邏輯是在此方法里,可以到 coreclr 中找一下這段代碼,簡化后如下:KoG28資訊網——每日最新資訊28at.com

HCIMPL2(void*, JIT_GetSharedNonGCThreadStaticBase, DomainLocalModule *pDomainLocalModule, DWORD dwClassDomainID){    FCALL_CONTRACT;    // Get the ModuleIndex    ModuleIndex index = pDomainLocalModule->GetModuleIndex();    // Get the relevant ThreadLocalModule    ThreadLocalModule * pThreadLocalModule = ThreadStatics::GetTLMIfExists(index);    // If the TLM has been allocated and the class has been marked as initialized,    // get the pointer to the non-GC statics base and return    if (pThreadLocalModule != NULL && pThreadLocalModule->IsPrecomputedClassInitialized(dwClassDomainID))        return (void*)pThreadLocalModule->GetPrecomputedNonGCStaticsBasePointer();    // If the TLM was not allocated or if the class was not marked as initialized    // then we have to go through the slow path    // Obtain the MethodTable    MethodTable * pMT = pDomainLocalModule->GetMethodTableFromClassDomainID(dwClassDomainID);    return HCCALL1(JIT_GetNonGCThreadStaticBase_Helper, pMT);}

這段代碼非常有意思,已經把 ThreadStatic 玩法的骨架圖給繪制出來了,大概意思是每個線程都有一個 ThreadLocalBlock 結構體,這個結構體下有一個 ThreadLocalModule 的字典,key 為 ModuleIndex, value 為 ThreadLocalModule,畫個簡圖如下:KoG28資訊網——每日最新資訊28at.com

圖片圖片KoG28資訊網——每日最新資訊28at.com

從圖中可以看到 num 是放在 ThreadLocalModule 中的,具體的說就是此結構的 m_pDataBlob 數組中,可以用 windbg 驗證下。KoG28資訊網——每日最新資訊28at.com

0:008> reax=03077810 ebx=08baf978 ecx=79d75c10 edx=03110568 esi=053faa18 edi=053fa9b8eip=08893746 esp=08baf8d8 ebp=08baf908 iopl=0         nv up ei pl zr na pe nccs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246ConsoleApp7!ConsoleApp7.Program.<>c.<Test>b__2_0+0x46:08893746 8b4814          mov     ecx,dword ptr [eax+14h] ds:002b:03077824=0000000a0:008> dt coreclr!ThreadLocalModule 03077810   +0x000 m_pDynamicClassTable : (null)    +0x004 m_aDynamicEntries : 0   +0x008 m_pGCStatics     : (null)    +0x00c m_pDataBlob      : [0]  ""0:008> dp 03077810+0x14 L103077824  0000000a

有了這些前置知識后,接下來就簡單了,如果當前的 ThreadLocalModule 不存在就會調用 JIT_GetNonGCThreadStaticBase_Helper 函數在 m_pTLMTable 字段中添加一項,接下來觀察下這個函數代碼,簡化如下:KoG28資訊網——每日最新資訊28at.com

HCIMPL1(void*, JIT_GetNonGCThreadStaticBase_Helper, MethodTable * pMT){    // Get the TLM    ThreadLocalModule * pThreadLocalModule = ThreadStatics::GetTLM(pMT);    // Check if the class constructor needs to be run    pThreadLocalModule->CheckRunClassInitThrowing(pMT);    // Lookup the non-GC statics base pointer    base = (void*) pMT->GetNonGCThreadStaticsBasePointer();    return base;}PTR_ThreadLocalModule ThreadStatics::GetTLM(ModuleIndex index, Module * pModule) //static{    // Get the TLM if it already exists    PTR_ThreadLocalModule pThreadLocalModule = ThreadStatics::GetTLMIfExists(index);    // If the TLM does not exist, create it now    if (pThreadLocalModule == NULL)    {        // Allocate and initialize the TLM, and add it to the TLB's table        pThreadLocalModule = AllocateAndInitTLM(index, pThreadLocalBlock, pModule);    }    return pThreadLocalModule;}

上面這段代碼的步驟很清楚。KoG28資訊網——每日最新資訊28at.com

  • 創建 ThreadLocalModule
  • 初始化 MethodTable 類型的字段 pMT

這個 pMT 非常重要,訓練營里的朋友都知道 MethodTable 是 C# 的 class 承載,言外之意就是判斷下這個 class 有沒有被初始化,如果沒有初始化那就調 靜態構造函數,接下來的問題是 class 到底是哪一個類呢?KoG28資訊網——每日最新資訊28at.com

結合剛才匯編中的 mov edx,4 以及源碼發現是取 IL 元數據中的 Program,參考代碼及截圖如下:KoG28資訊網——每日最新資訊28at.com

FORCEINLINE MethodTable * GetMethodTableFromClassDomainID(DWORD dwClassDomainID)    {        DWORD rid = (DWORD)(dwClassDomainID) + 1;        TypeHandle th = GetDomainFile()->GetModule()->LookupTypeDef(TokenFromRid(rid, mdtTypeDef));        MethodTable * pMT = th.AsMethodTable();        return pMT;    }

圖片圖片KoG28資訊網——每日最新資訊28at.com

也可以用 windbg 在 JIT_GetNonGCThreadStaticBase_Helper 方法的 return 處下一個斷點,參考如下:KoG28資訊網——每日最新資訊28at.com

0:008> r ecxecx=0564ef280:008> !dumpmt 0564ef28EEClass:             056d14d0Module:              0564db08Name:                ConsoleApp7.ProgrammdToken:             02000005File:                D:/code/MyApplication/ConsoleApp7/bin/x86/Debug/net6.0/ConsoleApp7.dllAssemblyLoadContext: Default ALC - The managed instance of this context doesn't exist yet.BaseSize:            0xcComponentSize:       0x0DynamicStatics:      falseContainsPointers:    falseSlots in VTable:     8Number of IFaces in IFaceMap: 0

到這里就真相大白了,thread1 在執行時,用 CheckRunClassInitThrowing 方法發現 Program 沒有被靜態構造過,所以就執行了,即 num=10 ,當 thread2 執行時,發現已經被構造過了,所以就不再執行靜態構造函數,所以就成了默認值 num=0。KoG28資訊網——每日最新資訊28at.com

3. 如何復驗你的結論

剛才我說 thread1 做了一個是否執行靜態構造的判斷,其實這里我可以做個手腳,在 Main 之前先把 Program 靜態函數給執行掉,按理說 thread1 和 thread2 此時都會是默認值 num=0,對不對,哈哈,試一試唄,簡化代碼如下:KoG28資訊網——每日最新資訊28at.com

internal class Program    {        [ThreadStatic]        public static int num = 10;        /// <summary>        /// 先于 main 執行        /// </summary>        static Program()        {        }        static void Main(string[] args)        {            Test();            Console.ReadLine();        }    }

圖片圖片KoG28資訊網——每日最新資訊28at.com

哈哈,此時都是 0 了,也就再次驗證了我的結論。KoG28資訊網——每日最新資訊28at.com

三:總結

在 C# 開發中經常會有一些疑惑,如果不了解匯編,C++ ,相信你會陷入到很多的魔法使用中而苦于不能獨自解惑的遺憾。KoG28資訊網——每日最新資訊28at.com

本文鏈接:http://m.www897cc.com/showinfo-26-68330-0.html讓我來告訴你,C# 線程本地存儲為什么線程間值不一樣

聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com

上一篇: Python的Graphlib庫,再也不用手敲圖結構了

下一篇: 八個Python開發者必備的PyCharm插件

標簽:
  • 熱門焦點
Top 日韩成人免费在线_国产成人一二_精品国产免费人成电影在线观..._日本一区二区三区久久久久久久久不
国产精品素人视频| 亚洲在线中文字幕| 国产午夜久久久久| 国自产拍偷拍福利精品免费一| 伊人成人开心激情综合网| 在线观看日韩av电影| 亚洲国产精品成人综合色在线婷婷| 亚洲黑丝在线| 亚洲视频第一页| 欧美中文字幕在线播放| 免费在线观看成人av| 欧美成人精品在线| 国产精品成人一区二区艾草| 国产视频精品xxxx| 亚洲韩日在线| 亚洲午夜激情| 久久精品视频导航| 欧美精品日韩综合在线| 国产精品―色哟哟| 影院欧美亚洲| 一本色道久久综合狠狠躁的推荐| 亚洲欧美国产另类| 久久影院午夜论| 加勒比av一区二区| 亚洲黄一区二区| 亚洲男人影院| 久热综合在线亚洲精品| 欧美日韩亚洲天堂| 国产一区日韩一区| 一本久久a久久免费精品不卡| 欧美亚洲一区二区在线| 欧美成人精品在线观看| 国产精品视频免费观看| 亚洲高清在线播放| 午夜激情久久久| 欧美激情小视频| 国产啪精品视频| 亚洲精选一区| 久久久美女艺术照精彩视频福利播放| 欧美日韩精品中文字幕| 黑人操亚洲美女惩罚| 宅男噜噜噜66国产日韩在线观看| 久久夜色精品国产亚洲aⅴ| 国产精品九九久久久久久久| 亚洲国产老妈| 久久狠狠婷婷| 国产精品午夜久久| 99国产一区| 欧美96在线丨欧| 狠狠噜噜久久| 欧美一区二区三区精品电影| 欧美午夜视频网站| 日韩午夜精品| 欧美国产在线观看| 在线观看av一区| 久久国产精品99国产精| 国产精品美女久久久浪潮软件| 亚洲精品综合在线| 欧美jizz19hd性欧美| 伊人久久大香线| 久久高清福利视频| 国产精品性做久久久久久| 999在线观看精品免费不卡网站| 久热精品视频在线免费观看 | 老色鬼精品视频在线观看播放| 国产精品国产馆在线真实露脸| 亚洲人成在线播放| av成人免费在线观看| 老司机aⅴ在线精品导航| 国产伦精品一区二区三| 最新日韩欧美| 蜜臀a∨国产成人精品| 国产午夜久久久久| 亚洲欧美日本国产有色| 欧美日韩dvd在线观看| 亚洲高清视频的网址| 久久久噜噜噜久噜久久| 久久九九免费视频| 国产精品男gay被猛男狂揉视频| 亚洲国产欧美在线| 午夜精品久久久久久99热软件| 欧美精品v国产精品v日韩精品| 红桃视频国产精品| 欧美一区二区视频在线观看| 欧美视频一区二区三区在线观看 | 亚洲精品日韩激情在线电影| 久久青草久久| 国产最新精品精品你懂的| 亚洲欧美日韩专区| 欧美性做爰猛烈叫床潮| 日韩视频中文字幕| 欧美精品高清视频| 最新亚洲视频| 欧美va天堂va视频va在线| 国语自产偷拍精品视频偷| 欧美在线一区二区三区| 国产乱码精品一区二区三| 亚洲综合视频一区| 国产精品高潮呻吟久久| 国产精品99久久久久久有的能看 | 欧美一区二区免费观在线| 国产精品一区二区久久久| 亚洲欧美国产精品桃花| 国产精品午夜久久| 久久精品国产77777蜜臀| 国产一区二区成人| 久久久久久9999| 在线观看日韩av| 欧美电影免费观看高清| 亚洲破处大片| 欧美日本不卡| 中文欧美在线视频| 欧美性大战久久久久| 欧美一区二区成人| 国内成人精品一区| 久久亚洲综合色一区二区三区| 在线 亚洲欧美在线综合一区| 久久一二三四| 亚洲精品极品| 欧美日韩国产欧| 一本色道久久88亚洲综合88| 欧美日韩成人一区二区| 亚洲综合国产精品| 国产亚洲欧美一区| 久久伊人精品天天| 亚洲人成人一区二区三区| 欧美日本中文字幕| 亚洲综合精品自拍| 国产综合色一区二区三区| 老司机精品福利视频| 亚洲免费精品| 国产精品嫩草99a| 久久精品30| 亚洲黄色片网站| 欧美日韩一区二区在线播放| 午夜精品国产更新| 一区免费观看视频| 欧美日韩高清区| 午夜久久福利| 黄色小说综合网站| 欧美精品一区二区三| 亚洲欧美另类国产| 伊人精品久久久久7777| 欧美日韩国产免费| 欧美影视一区| 亚洲人成绝费网站色www| 欧美—级a级欧美特级ar全黄| 亚洲欧美国产三级| 亚洲电影免费在线| 国产精品a级| 久久久久久亚洲精品杨幂换脸| 亚洲乱码久久| 国产视频精品免费播放| 欧美电影打屁股sp| 午夜一区不卡| 国内精品久久久久久影视8| 欧美午夜精品久久久| 久久久精品五月天| 一区二区三区久久精品| 国产亚洲网站| 欧美日韩亚洲一区二区| 久久精品视频免费| 一区二区三区国产在线观看| 激情成人在线视频| 国产精品va在线播放| 久久精品久久99精品久久| 9人人澡人人爽人人精品| 韩国免费一区| 国产精品久久久91| 免费看的黄色欧美网站| 亚洲综合三区| 亚洲美女av电影| 一区二区三区自拍| 国产精品免费一区二区三区观看| 欧美电影免费观看大全| 欧美一区二区大片| 一区二区av| 亚洲激情在线播放| 一区精品在线播放| 国产精品综合| 欧美日韩中文另类| 欧美va亚洲va香蕉在线| 欧美影院成人| 亚洲图片自拍偷拍| 亚洲国产成人在线| 一色屋精品视频在线观看网站 | 在线精品国精品国产尤物884a| 国产精品腿扒开做爽爽爽挤奶网站| 欧美成人精品一区| 久久久999国产| 午夜久久久久久| 亚洲桃花岛网站| 亚洲国产mv| 1024日韩| 激情欧美一区二区三区| 国产欧美亚洲一区| 国产精品都在这里| 欧美精品啪啪| 欧美xx69| 欧美电影在线观看| 蜜月aⅴ免费一区二区三区 | 久久精品人人做人人综合| 欧美一区亚洲| 亚洲综合电影一区二区三区|