精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

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

開發(fā) 前端
,為什么用 ThreadStatic 標(biāo)記的字段,只有第一個線程拿到了初始值,其他線程都是默認(rèn)值,讓我能不能幫他解答一下,尼瑪,我也不是神仙什么都懂,既然問了,那我試著幫他解答一下,也給后面類似疑問的朋友解個惑吧。

一:背景

1. 講故事

有朋友在微信里面問我,為什么用 ThreadStatic 標(biāo)記的字段,只有第一個線程拿到了初始值,其他線程都是默認(rèn)值,讓我能不能幫他解答一下,尼瑪,我也不是神仙什么都懂,既然問了,那我試著幫他解答一下,也給后面類似疑問的朋友解個惑吧。

二:為什么值不一樣

1. 問題復(fù)現(xiàn)

為了方便講述,定義一個 ThreadStatic 的變量,然后用多個線程去訪問,參考代碼如下:

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

圖片圖片

從代碼中可以看到,確實如朋友所說,一個是num=10,一個是num=0 ,那為什么會出現(xiàn)這樣的情況呢?

2. 從匯編上尋找答案

作為C#程序員,真的需要掌握一點匯編,往往就能找到問題的突破口,先看一下thread1 中的 var j = num;所對應(yīng)的匯編代碼,參考如下:

D:\code\MyApplication\ConsoleApp7\Program.cs @ 27:
08893737 b9a0dd6808      mov     ecx,868DDA0h
0889373c ba04000000      mov     edx,4
08893741 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 函數(shù)的返回值,言外之意核心邏輯是在此方法里,可以到 coreclr 中找一下這段代碼,簡化后如下:

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

這段代碼非常有意思,已經(jīng)把 ThreadStatic 玩法的骨架圖給繪制出來了,大概意思是每個線程都有一個 ThreadLocalBlock 結(jié)構(gòu)體,這個結(jié)構(gòu)體下有一個 ThreadLocalModule 的字典,key 為 ModuleIndex, value 為 ThreadLocalModule,畫個簡圖如下:

圖片圖片

從圖中可以看到 num 是放在 ThreadLocalModule 中的,具體的說就是此結(jié)構(gòu)的 m_pDataBlob 數(shù)組中,可以用 windbg 驗證下。

0:008> r
eax=03077810 ebx=08baf978 ecx=79d75c10 edx=03110568 esi=053faa18 edi=053fa9b8
eip=08893746 esp=08baf8d8 ebp=08baf908 iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
ConsoleApp7!ConsoleApp7.Program.<>c.<Test>b__2_0+0x46:
08893746 8b4814          mov     ecx,dword ptr [eax+14h] ds:002b:03077824=0000000a

0: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 L1
03077824  0000000a

有了這些前置知識后,接下來就簡單了,如果當(dāng)前的 ThreadLocalModule 不存在就會調(diào)用 JIT_GetNonGCThreadStaticBase_Helper 函數(shù)在 m_pTLMTable 字段中添加一項,接下來觀察下這個函數(shù)代碼,簡化如下:

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

上面這段代碼的步驟很清楚。

  • 創(chuàng)建 ThreadLocalModule
  • 初始化 MethodTable 類型的字段 pMT

這個 pMT 非常重要,訓(xùn)練營里的朋友都知道 MethodTable 是 C# 的 class 承載,言外之意就是判斷下這個 class 有沒有被初始化,如果沒有初始化那就調(diào) 靜態(tài)構(gòu)造函數(shù),接下來的問題是 class 到底是哪一個類呢?

結(jié)合剛才匯編中的 mov edx,4 以及源碼發(fā)現(xiàn)是取 IL 元數(shù)據(jù)中的 Program,參考代碼及截圖如下:

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

圖片圖片

也可以用 windbg 在 JIT_GetNonGCThreadStaticBase_Helper 方法的 return 處下一個斷點,參考如下:

0:008> r ecx
ecx=0564ef28
0:008> !dumpmt 0564ef28
EEClass:             056d14d0
Module:              0564db08
Name:                ConsoleApp7.Program
mdToken:             02000005
File:                D:\code\MyApplication\ConsoleApp7\bin\x86\Debug\net6.0\ConsoleApp7.dll
AssemblyLoadContext: Default ALC - The managed instance of this context doesn't exist yet.
BaseSize:            0xc
ComponentSize:       0x0
DynamicStatics:      false
ContainsPointers:    false
Slots in VTable:     8
Number of IFaces in IFaceMap: 0

到這里就真相大白了,thread1 在執(zhí)行時,用 CheckRunClassInitThrowing 方法發(fā)現(xiàn) Program 沒有被靜態(tài)構(gòu)造過,所以就執(zhí)行了,即 num=10 ,當(dāng) thread2 執(zhí)行時,發(fā)現(xiàn)已經(jīng)被構(gòu)造過了,所以就不再執(zhí)行靜態(tài)構(gòu)造函數(shù),所以就成了默認(rèn)值 num=0。

3. 如何復(fù)驗?zāi)愕慕Y(jié)論

剛才我說 thread1 做了一個是否執(zhí)行靜態(tài)構(gòu)造的判斷,其實這里我可以做個手腳,在 Main 之前先把 Program 靜態(tài)函數(shù)給執(zhí)行掉,按理說 thread1 和 thread2 此時都會是默認(rèn)值 num=0,對不對,哈哈,試一試唄,簡化代碼如下:

internal class Program
    {
        [ThreadStatic]
        public static int num = 10;

        /// <summary>
        /// 先于 main 執(zhí)行
        /// </summary>
        static Program()
        {
        }

        static void Main(string[] args)
        {
            Test();

            Console.ReadLine();
        }
    }

圖片圖片

哈哈,此時都是 0 了,也就再次驗證了我的結(jié)論。

三:總結(jié)

在 C# 開發(fā)中經(jīng)常會有一些疑惑,如果不了解匯編,C++ ,相信你會陷入到很多的魔法使用中而苦于不能獨自解惑的遺憾。

責(zé)任編輯:武曉燕 來源: 一線碼農(nóng)聊技術(shù)
相關(guān)推薦

2012-12-21 10:42:49

數(shù)據(jù)分析中土世界數(shù)據(jù)可視化項目

2020-02-14 14:36:23

DevOps落地認(rèn)知

2012-12-20 10:17:32

IT運維

2012-03-07 17:24:10

戴爾咨詢

2021-07-12 23:53:22

Python交換變量

2017-11-03 07:57:19

2023-03-20 08:19:23

GPT-4OpenAI

2021-05-14 10:28:54

線程協(xié)程數(shù)量

2018-07-10 11:05:55

Emoji蘋果Google

2017-05-25 15:02:46

聯(lián)宇益通SD-WAN

2015-10-19 12:33:01

華三/新IT

2016-05-09 18:40:26

VIP客戶緝拿

2018-02-28 10:16:51

程序員技能 PHP

2010-11-24 20:18:48

Microsoft L統(tǒng)一溝通

2012-07-18 02:05:02

函數(shù)語言編程語言

2022-04-29 08:00:36

web3區(qū)塊鏈比特幣

2018-02-27 12:01:12

程序員PHP編程語言

2012-04-27 09:19:08

2018-05-09 15:42:24

新零售

2021-12-23 15:11:46

Web 3.0元宇宙Metaverse
點贊
收藏

51CTO技術(shù)棧公眾號

免费网站在线观看黄| 波多野结衣成人在线| 国产精品无码一区二区三区免费| 50度灰在线| 国产suv精品一区二区883| 久久免费视频网| 欧美激情中文字幕乱码免费| 日本欧美韩国国产| 中文字幕xxx| 欧美日韩国产成人在线观看| 日日摸夜夜添夜夜添国产精品| 性中国xxx极品hd| 免费cad大片在线观看| 91av免费观看91av精品在线| 黄色大片中文字幕| 天天操天天操天天| 日韩激情一区二区| 色悠悠久久88| 美女露出粉嫩尿囗让男人桶| 小草在线视频免费播放| 国产精品天干天干在线综合| 亚洲自拍在线观看| 日本熟妇乱子伦xxxx| 偷拍自拍一区| 在线综合视频播放| 国产女大学生av| 女女色综合影院| 不卡的av电影| 91精品久久久久久久久久另类| 久久免费播放视频| 精品国产乱码久久久久久果冻传媒 | 中国1级黄色片| 欧美香蕉爽爽人人爽| 久久午夜电影| 欧美精品在线免费| 精品人妻一区二区三区四区| 色妞ww精品视频7777| 欧美三级电影网站| 一本久道中文无码字幕av| 黄色的视频在线观看| 国产精品久久久久一区| 日产精品一线二线三线芒果| 婷婷视频在线观看| 国产精品白丝jk黑袜喷水| 国产精品揄拍一区二区| 毛片在线免费播放| 久久久久久自在自线| 91极品女神在线| 精品少妇theporn| 亚洲精品tv久久久久久久久久| 亚洲香蕉av在线一区二区三区| 中文字幕日韩三级片| 福利在线一区| 欧美成va人片在线观看| 一本之道在线视频| av在线精品| 91精品在线观看入口| 国产在线观看中文字幕| 国产免费区一区二区三视频免费 | 久久久久久久黄色| 欧美日韩蜜桃| 国内精品久久久久影院 日本资源| 加勒比av在线播放| 一区视频在线| 91禁国产网站| 天天干天天操天天爱| 久久精品人人| 日韩免费高清在线观看| 自拍偷拍色综合| 久久精品国内一区二区三区| 成人黄色免费网站在线观看| 国产丝袜在线视频| 福利一区在线观看| 好看的日韩精品| 经典三级在线| 中文字幕亚洲欧美在线不卡| 穿情趣内衣被c到高潮视频| 污网站在线免费看| 亚洲国产精品久久不卡毛片| 国产主播在线看| 精品成人av| 欧美一级黄色片| 四虎成人免费视频| 国产日韩欧美一区二区三区| 最新国产精品拍自在线播放| 色欲人妻综合网| 国产一区美女| 日本一本a高清免费不卡| 中文字幕在线播放av| 精品在线你懂的| 国产一区二区三区黄| 成人77777| 一区二区高清视频在线观看| 欧美日韩中文在线视频| 色综合一区二区日本韩国亚洲| 精品国产一区二区亚洲人成毛片 | 欧美福利在线观看| 秋霞精品一区二区三区| 国产一区二区免费视频| 久久精品国产一区二区三区不卡| 91在线看片| 亚洲一区二区五区| 91香蕉视频污版| 一区三区自拍| 中文字幕亚洲第一| 亚洲国产精一区二区三区性色| 日韩av成人高清| 国产91aaa| 91最新在线| 岛国av一区二区三区| 亚洲一区二区偷拍| 国产一区二区三区探花| 欧美极度另类性三渗透| 欧美在线视频精品| av一本久道久久综合久久鬼色| 手机福利在线视频| 欧美舌奴丨vk视频| 亚洲福利在线视频| 九九热最新地址| 美女性感视频久久| 欧美日韩天天操| 岛国片av在线| 欧美一区二区在线观看| 国产真人做爰视频免费| 亚洲在线免费| 99精品99久久久久久宅男| av在线电影观看| 色综合久久88色综合天天6| 美女黄色一级视频| 欧美精品一卡| 国产在线精品一区免费香蕉| 久久久久久青草| 婷婷六月综合网| 性感美女一区二区三区| 亚洲欧洲日韩| 国产视频福利一区| jizz日韩| 在线观看91精品国产入口| 日本黄色动态图| 亚洲国内精品| 国产精品美女诱惑| 欧美草逼视频| 日韩午夜激情av| 青娱乐91视频| 成人少妇影院yyyy| 波多野结衣av一区二区全免费观看| 伊人亚洲精品| 久久在线视频在线| 国产精品久久久午夜夜伦鲁鲁| 中文在线免费一区三区高中清不卡| av动漫免费看| 蜜桃国内精品久久久久软件9| 9.1国产丝袜在线观看| 亚州男人的天堂| 大荫蒂欧美视频另类xxxx| 在线免费播放av| 国产一区导航| 欧美日韩免费观看一区| 中文.日本.精品| 色香阁99久久精品久久久| 97成人免费视频| 亚洲精品国产视频| 成人区人妻精品一区二| 在线亚洲一区| 欧美自拍资源在线| 免费在线成人激情电影| 日韩日本欧美亚洲| 精品国产亚洲一区二区麻豆| 亚洲一级二级在线| 自拍视频一区二区| 视频一区二区中文字幕| 亚洲精品日韩精品| 精品一区二区三区视频在线播放 | 久久精品国产秦先生| 三年中国中文在线观看免费播放 | 久久青青草原一区二区| 欧美黑人巨大xxxxx| 最近中文字幕mv在线一区二区三区四区| 国产一区二区小视频| 亚洲国产日产av| 91久久免费视频| 精品一二三四在线| 欧美视频免费看欧美视频| 神马午夜久久| 成人欧美一区二区三区在线湿哒哒| 中文字幕有码在线观看| 精品五月天久久| 国产乱子伦精品无码码专区| 亚洲成人av福利| 亚洲色成人网站www永久四虎| 国内久久婷婷综合| 1024av视频| 亚洲成人精品| 久久久久se| 精品国产一区二区三区性色av| 91av在线看| 黄色在线免费| 亚洲网站在线播放| 超碰在线观看99| 欧洲一区在线电影| 欧美日韩精品在线观看视频| 久久欧美一区二区| 人妻巨大乳一二三区| 可以看av的网站久久看| 屁屁影院ccyy国产第一页| 国产区精品区| 国产亚洲第一区| 99综合99| 国产精品丝袜高跟| 午夜裸体女人视频网站在线观看| 久久久999国产| 黄色电影免费在线看| 亚洲精品在线免费播放| 国产乱淫片视频| 欧美在线免费观看亚洲| 在线观看精品国产| 一区二区视频免费在线观看| 我不卡一区二区| 91在线播放网址| www.欧美com| 久久丁香综合五月国产三级网站| 欧美牲交a欧美牲交aⅴ免费下载| 亚洲特色特黄| 九一免费在线观看| 99免费精品| 日韩高清dvd| 欧亚精品一区| 久久国产精品99久久久久久丝袜| 激情综合五月| 91精品视频网站| 欧美高清免费| 国产精品视频成人| 69堂精品视频在线播放| 欧美一区二区视频97| 日韩伦理福利| 欧美一级在线播放| 免费成人在线电影| 2019最新中文字幕| 日产福利视频在线观看| 18性欧美xxxⅹ性满足| 99色在线观看| 久久人人爽人人爽人人片av高请 | 人人妻人人澡人人爽人人欧美一区 | 深夜福利一区二区| 第一视频专区在线| 影音先锋日韩有码| gogogo高清在线观看免费完整版| 国产午夜精品一区二区三区| 高清福利在线观看| 中文字幕日韩欧美在线视频| 在线免费观看黄色av| 日韩在线免费av| 黄色在线论坛| 欧美大荫蒂xxx| 久久五月精品中文字幕| 久久久综合av| 漫画在线观看av| 日韩美女免费观看| 99精品在免费线偷拍| 国产日韩av在线| 免费一区二区三区在线视频| 高清不卡一区二区三区| 女仆av观看一区| 日本在线观看一区二区| 欧美a级片视频| 国产盗摄视频在线观看| 国产精品mv在线观看| 日本十八禁视频无遮挡| 久久午夜电影| 亚洲精品成人在线播放| 成人永久看片免费视频天堂| 欧美成人午夜精品免费| 国产精品久久国产精麻豆99网站| 无码黑人精品一区二区| 午夜视频一区二区| 国产一级精品毛片| 日韩欧美一级二级| 毛片免费在线| 精品国产拍在线观看| 激情av在线播放| 日韩免费在线播放| 国产专区精品| 久久亚洲国产精品日日av夜夜| 波多野结衣一区| 水蜜桃在线免费观看| 国产欧美日韩综合一区在线播放| 久久婷婷国产91天堂综合精品| 久久精品99国产精品| 国产精品一区二区无码对白| 国产亚洲综合在线| 日韩欧美中文字幕视频| 欧美午夜无遮挡| av网站在线观看免费| 精品网站999www| 成人午夜在线影视| 国产91精品在线播放| 狂野欧美xxxx韩国少妇| 欧美性色黄大片人与善| 欧美性久久久| 蜜桃免费在线视频| 99久久精品久久久久久清纯| 强制高潮抽搐sm调教高h| 婷婷成人综合网| 99视频国产精品免费观看a| 亚洲天堂av在线播放| 丁香花高清在线观看完整版| 成人美女免费网站视频| 蜜桃成人av| 亚洲熟妇无码一区二区三区| 国内精品免费在线观看| 色哟哟精品观看| 亚洲观看高清完整版在线观看| 97av免费视频| 亚洲人成网站免费播放| av影院在线| 91久久国产综合久久蜜月精品| 日本精品黄色| 99精品视频播放| caoporn国产一区二区| 精品欧美一区二区久久久久| 欧美日韩三级视频| 青青色在线视频| 91国内在线视频| 国产精品网站在线看| 日韩免费在线观看av| 狠狠色丁香久久婷婷综合_中 | 全部免费毛片在线播放一个| 久久精品视频一| 欧美aaaaaaaa| 午夜精品一区二区在线观看 | 欧美日韩精品区别| 欧美国产综合色视频| 极品国产91在线网站| 亚洲精品视频网上网址在线观看| 爱啪视频在线观看视频免费| 91久久精品国产91久久| 色综合咪咪久久网| 国产精品一区二区羞羞答答| 久久久另类综合| 天天干天天干天天操| 日韩精品在线免费播放| 水蜜桃在线视频| 久99久在线| 国产农村妇女精品一二区| 丰满少妇在线观看资源站| 色综合天天性综合| 蜜桃成人在线视频| 日韩av观看网址| 国产探花在线精品| 久久精品影视大全| 国产精品福利一区| 国产巨乳在线观看| 欧美激情欧美激情在线五月| 凹凸av导航大全精品| 一二三四视频社区在线| 91亚洲大成网污www| 亚洲欧美综合自拍| 国产一区二区三区欧美| 婷婷成人av| 欧美黄色免费网址| 播五月开心婷婷综合| 好看的av在线| 一区二区成人精品| 99亚洲男女激情在线观看| av在线观看地址| 99re成人在线| 五月天中文字幕| 久久中文精品视频| 精品国产一区二区三区成人影院| 黄色免费视频大全| 欧美韩国日本不卡| www.黄色av| 国产999精品视频| 91久久国产| 麻豆精品国产传媒av| 色婷婷亚洲综合| 国产在线激情| 黄色国产精品一区二区三区| 肉肉av福利一精品导航| 亚洲一区电影在线观看| 亚洲高清色综合| 欧美日韩视频免费观看| 男人天堂成人网| 99精品视频在线免费观看| 中文字幕在线播放av| 久久久久女教师免费一区| 亚洲精品小区久久久久久| 中文字幕免费高清在线| 亚洲第一精品在线| 97最新国自产拍视频在线完整在线看| 99国内精品久久久久久久软件| 久久一区激情| 九九视频免费在线观看| 亚洲片国产一区一级在线观看| 国产精品国产亚洲精品| 1024av视频| 一区二区三区免费在线观看| 国产专区在线| 国产亚洲一区二区三区在线播放| 蜜臀av一区二区在线观看|