日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > C# >内容正文

C#

被 C# 的 ThreadStatic 标记的静态变量,都存放在哪里了?

發布時間:2023/12/4 C# 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 被 C# 的 ThreadStatic 标记的静态变量,都存放在哪里了? 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

一:背景

1. 講故事

前幾天公號里有一位朋友留言說,你windbg玩的溜,能幫我分析下被 ThreadStatic 修飾的變量到底存放在哪里嗎?能不能幫我挖出來????????????,其實這個問題問的挺深的,玩高級語言的朋友相信很少有接觸到這個的,雖然很多朋友都知道這個特性怎么用,當然我也沒特別研究這個,既然要回答這個問題,我得研究研究回答之!為了更好的普適性,先從簡單的說起!

二:ThreadStatic 的用法

1. 普通的 static 變量

相信很多朋友在代碼中都使用過 static 變量,它的好處多多,比如說我經常會用 static 去做一個進程級緩存,從而提高程序的性能,當然你也可以作為一個非常好的一級緩存,如下代碼:

public?class?Test{public?static?Dictionary<int,?string>?cachedDict?=?new?Dictionary<int,?string>();}

剛才我也說到了,這是一個進程級的緩存,多個線程都看得見,所以在多線程的環境下,你需要特別注意同步的問題。要么使用鎖,要么使用 ConcurrentDictionary,我覺得這也是一個思維定式,很多時候思維總是在現有基礎上去修補,去亡羊補牢,而沒有跳出這個思維從根基上去處理,說這么多是什么意思呢?我舉一個例子:

在市面上常見的鏈式跟蹤框架中,比如說:Zikpin,SkyWalking,會使用一些集合去存儲跟蹤當前線程的一些鏈路信息,比如說 A -> B -> C -> D -> B -> A,常規的思維就像上面說的一樣,定義一個全局 cachedDict,然后使用各種同步機制,其實你也可以降低 cachedDict 的訪問作用域,將 全局訪問 改成 Thread級訪問,這難道不是更好的解決思路嗎?

2. 用 ThreadStatic 標記 static 變量

要想做到 Thread級作用域,實現起來非常簡單,在 cachedDict 上打一個 ThreadStatic 特性即可,修改代碼如下:

public?class?Test{[ThreadStatic]public?static?Dictionary<int,?string>?cachedDict?=?new?Dictionary<int,?string>();}

接下來可以開多個線程給 cachedDict 灌數據,看看 dict 是不是 Thread級作用域,實現代碼如下:

class?Program{static?void?Main(string[]?args){var?task1?=?Task.Run(()?=>{if?(Test.cachedDict?==?null)?Test.cachedDict?=?new?Dictionary<int,?string>();Test.cachedDict.Add(1,?"mary");Test.cachedDict.Add(2,?"john");Console.WriteLine($"thread={Thread.CurrentThread.ManagedThreadId}?的?dict?有記錄:?{Test.cachedDict.Count}");});var?task2?=?Task.Run(()?=>{if?(Test.cachedDict?==?null)?Test.cachedDict?=?new?Dictionary<int,?string>();Test.cachedDict.Add(3,?"python");Test.cachedDict.Add(4,?"jaskson");Test.cachedDict.Add(5,?"elen");Console.WriteLine($"thread={Thread.CurrentThread.ManagedThreadId}?的?dict?有記錄:?{Test.cachedDict.Count}");});Console.ReadLine();}}public?class?Test{[ThreadStatic]public?static?Dictionary<int,?string>?cachedDict?=?new?Dictionary<int,?string>();}

從結果來看,確實是一個 Thread 級,而且還避免了線程間同步開銷,哈哈????,這么神奇的東西,難怪有讀者想看看底層到底是怎么實現的。

三:用 Windbg 挖 ThreadStatic

1. 對 TEB 和 TLS 的認識

  • TEB (Thread Environment Block)

每一個線程都有一份屬于自己專屬的私有數據,這些數據就放在 Thread 的 TEB 中,如果你想看的話,可以在 windbg 中打印出來。

0:000>?!teb TEB?at?0000001e1cdd3000ExceptionList:????????0000000000000000StackBase:????????????0000001e1cf80000StackLimit:???????????0000001e1cf6e000SubSystemTib:?????????0000000000000000FiberData:????????????0000000000001e00ArbitraryUserPointer:?0000000000000000Self:?????????????????0000001e1cdd3000EnvironmentPointer:???0000000000000000ClientId:?????????????0000000000005980?.?0000000000005aa8RpcHandle:????????????0000000000000000Tls?Storage:??????????000001b599d06db0PEB?Address:??????????0000001e1cdd2000LastErrorValue:???????0LastStatusValue:??????c0000139Count?Owned?Locks:????0HardErrorMode:????????0

從 teb 的結構中可以看出,既有 線程本地存儲(TLS),也有異常相關信息的存儲 (ExceptionList) 等等相關信息。

  • TLS (Thread Local Storage)

進程會在啟動后給 TLS 分配總共 1088 個槽位,每個線程都會分配一個專屬的 tlsindex 索引,并且擁有一組 slots 槽位,可以用 windbg 去驗證一下。

0:000>?!tls Usage: tls?<slot>?[teb]slot:??-1?to?dump?all?allocated?slots{0-0n1088}?to?dump?specific?slotteb:???<empty>?for?current?thread0?for?all?threads?in?this?process<teb?address>?(not?threadid)?to?dump?for?specific?thread. 0:000>?!tls?-1 TLS?slots?on?thread:?5980.5aa8 0x0000?:?0000000000000000 0x0001?:?0000000000000000 0x0002?:?0000000000000000 0x0003?:?0000000000000000 0x0004?:?0000000000000000 ... 0x0019?:?0000000000000000 0x0040?:?00000000000000000:000>?!t????????????????????????????????????????????????????????????????????????????????????????????????????????Lock??DBG???ID?OSID?ThreadOBJ???????????State?GC?Mode?????GC?Alloc?Context??????????????????Domain???????????Count?Apt?Exception0????1?5aa8?000001B599CEED90????2a020?Preemptive??000001B59B9042F8:000001B59B905358?000001b599cdb130?1?????MTA?5????2??90c?000001B599CF4930????2b220?Preemptive??0000000000000000:0000000000000000?000001b599cdb130?0?????MTA?(Finalizer)?7????3???74?000001B59B7272A0??102a220?Preemptive??0000000000000000:0000000000000000?000001b599cdb130?0?????MTA?(Threadpool?Worker)?9????4?2058?000001B59B7BAFF0??1029220?Preemptive??0000000000000000:0000000000000000?000001b599cdb130?0?????MTA?(Threadpool?Worker)?

從上面的 {0-0n1088} to dump specific slot 中可以看出,進程中總會有 1088 個槽位,而且當前主線程 5aa8 擁有 27 個 slot 槽位。

好了,基本概念介紹完了,接下來準備分析一下匯編代碼了。

2. 從匯編代碼中尋找答案

為了更好的用 windbg 去挖,我就定義一個簡單的 ThreadStatic int 變量,代碼如下:

class?Program{[ThreadStatic]public?static?int?i?=?0;static?void?Main(string[]?args){i?=?10;???//?12?linevar?num?=?i;Console.ReadLine();}}

接下來用 !U 反匯編一下 Main 函數的代碼,著重看一下第 12 行代碼的 i = 10;。

0:000>?!U?/d?00007ffbe0ae0ffb E:\net5\ConsoleApp5\ConsoleApp5\Program.cs?@?12: 00007ffb`e0ae0fd6?48b9b0fbb7e0fb7f0000?mov?rcx,7FFBE0B7FBB0h 00007ffb`e0ae0fe0?ba01000000??????mov?????edx,1 00007ffb`e0ae0fe5?e89657a95f??????call????coreclr!JIT_GetSharedNonGCThreadStaticBase?(00007ffc`40576780) 00007ffb`e0ae0fea?c7401c0a000000??mov?????dword?ptr?[rax+1Ch],0Ah

從匯編指令上來看,最后的 10 賦給了 rax+1Ch 的低32位,那 rax 的地址從哪里來的呢?可以看出核心邏輯在 JIT_GetSharedNonGCThreadStaticBase 方法內,接下來就得研究一下這個方法都干嘛了。

3. 調試核心函數 JIT_GetSharedNonGCThreadStaticBase

接下來在第 12 處設置一個斷點 !bpmd Program.cs:12 處,方法的簡化匯編代碼如下:

coreclr!JIT_GetSharedNonGCThreadStaticBase: 00007ffc`2c38679a?448b0dd7894300?????????mov?????r9d,?dword?ptr?[coreclr!_tls_index?(00007ffc`2c7bf178)] 00007ffc`2c3867a1?654c8b042558000000?????mov?????r8,?qword?ptr?gs:[58h] 00007ffc`2c3867aa?b908000000?????????????mov?????ecx,?8 00007ffc`2c3867af?4f8b04c8???????????????mov?????r8,?qword?ptr?[r8+r9*8] 00007ffc`2c3867b3?4e8b0401???????????????mov?????r8,?qword?ptr?[rcx+r8] 00007ffc`2c3867b7?493b8060040000?????????cmp?????rax,?qword?ptr?[r8+460h] 00007ffc`2c3867be?732b???????????????????jae?????coreclr!JIT_GetSharedNonGCThreadStaticBase+0x6b?(00007ffc`2c3867eb) 00007ffc`2c3867c0?4d8b8058040000?????????mov?????r8,?qword?ptr?[r8+458h] 00007ffc`2c3867c7?498b04c0???????????????mov?????rax,?qword?ptr?[r8+rax*8] 00007ffc`2c3867cb?4885c0?????????????????test????rax,?rax 00007ffc`2c3867ce?741b???????????????????je??????coreclr!JIT_GetSharedNonGCThreadStaticBase+0x6b?(00007ffc`2c3867eb) 00007ffc`2c3867d0?8bca???????????????????mov?????ecx,?edx 00007ffc`2c3867d2?f644011801?????????????test????byte?ptr?[rcx+rax+18h],?1 00007ffc`2c3867d7?7412???????????????????je??????coreclr!JIT_GetSharedNonGCThreadStaticBase+0x6b?(00007ffc`2c3867eb) 00007ffc`2c3867d9?488b4c2420?????????????mov?????rcx,?qword?ptr?[rsp+20h] 00007ffc`2c3867de?4833cc?????????????????xor?????rcx,?rsp 00007ffc`2c3867e1?e89a170600?????????????call????coreclr!__security_check_cookie?(00007ffc`2c3e7f80) 00007ffc`2c3867e6?4883c438???????????????add?????rsp,?38h 00007ffc`2c3867ea?c3?????????????????????ret??

接下來我仔細分析下這里的 mov 操作。

1) dword ptr [coreclr!_tls_index (00007ffc`2c7bf178)]

這個很簡單,獲取該線程專屬的 tls_index 索引

2) qword ptr gs:[58h]

這里的 gs:[58h] 是什么意思呢?應該有朋友知道,gs寄存器 是專門用于存放當前線程的 teb 地址,后面的 58 表示在 teb 地址上的偏移量,那問題來了,這個地址到底指向誰了呢?其實你可以把 teb 的數據結構給打印出來就明白了。

0:000>?dt?teb coreclr!TEB+0x000?NtTib????????????:?_NT_TIB+0x038?EnvironmentPointer?:?Ptr64?Void+0x040?ClientId?????????:?_CLIENT_ID+0x050?ActiveRpcHandle??:?Ptr64?Void+0x058?ThreadLocalStoragePointer?:?Ptr64?Void+0x060?ProcessEnvironmentBlock?:?Ptr64?_PEB...

上面這句 +0x058 ThreadLocalStoragePointer : Ptr64 Void 可以看出,其實就是指向 ThreadLocalStoragePointer 。

3) qword ptr [r8+r9*8]

有了前兩步的基礎,這句匯編就很簡單了,它做了一個索引操作: ThreadLocalStoragePointer[tls_index] ,對不對,從而獲取屬于該線程的 tls 內容,這個 ThreadStatic 的變量就會存放在這個數組的某一個內存塊中。

后續還有一些計算偏移的邏輯運算都基于這個 ?ThreadLocalStoragePointer[tls_index] 之上,方法調用繞來繞去,匯編沒法看哈 ????????????

四:總結

總的來說,可以確定 ThreadStatic 變量 確實是存放在 TEB 的 ThreadLocalStoragePointer 數組中,這幾天 NET5 的 CoreCLR 沒有編譯成功,大家如果感興趣,可以 調試 CoreCLR + 匯編 做更深入的挖掘!

總結

以上是生活随笔為你收集整理的被 C# 的 ThreadStatic 标记的静态变量,都存放在哪里了?的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 欧美激情视频网站 | 日韩精品成人免费观看视频 | 亚洲高清在线一区 | 欧洲一区二区在线 | 国产毛茸茸 | 亚洲色吧 | 精品麻豆av | 欧美精品免费播放 | 国产精品不卡av | 成人娱乐网 | 玉势 (1v1 高h) | 最近更新中文字幕 | 午夜诱惑痒痒网 | 亚洲欧美日韩国产一区二区 | 99嫩草 | 中日韩在线视频 | 精品欧美在线 | 精品国产一区在线 | 亚洲妇女无套内射精 | 成人网在线播放 | 红桃成人在线 | www欧美 | 一区二区三区视频网 | 亚洲淫欲 | 国产a级片视频 | 亚洲精品视频在线观看免费视频 | 中文字幕一区二区三区人妻不卡 | 神马久久久久久 | 一区二区三区视频免费 | 影音先锋男人站 | 欧美日韩亚洲一区 | 8ppav| 五月激情综合婷婷 | 亚洲欧美一 | 污网在线看 | 中文字幕亚洲欧美日韩 | 风韵丰满熟妇啪啪区老熟熟女 | 熟妇人妻一区二区三区四区 | 欧美视频一级 | 久久久久亚洲av无码专区 | 国产网红在线 | 日日插夜夜爽 | 婷婷久久综合 | 日本xx视频 | 99国产精品久久久久久久成人 | 国产做爰全免费的视频软件 | 日本一区二区三区视频免费看 | 福利一区三区 | 国产免费一区二区三区三州老师 | 久久一区欧美 | 国产日韩欧美激情 | xxxx日本黄色 | 中文字幕av解说 | 欧美 亚洲 一区 | 国产午夜手机精彩视频 | 国产高清黄色 | 一级毛片aa | 97在线视频免费 | 久久免费视频观看 | 天天艹 | 日韩啪啪网 | 国产欧美一区二区三区视频在线观看 | 美女被娇喘流出白 | 午夜免费毛片 | 国产一区综合 | 亚洲av无一区二区三区怡春院 | 久久久久爱 | 国产91精品一区二区麻豆亚洲 | 人妖videosex高潮另类 | 色屁屁草草影院ccyycom | av日韩国产| 九九碰| 国产一区欧美一区 | 啪啪中文字幕 | 国产精品欧美性爱 | caoprom在线视频 | 欧美在线看 | 欧美一区二区三区四 | 少妇被躁爽到高潮无码人狍大战 | 五月婷婷天 | 美女赤身免费网站 | 日韩一级二级三级 | 亚洲GV成人无码久久精品 | 97网站 | 日韩欧美亚洲综合 | 九月激情网 | 国产精品第一国产精品 | 8x8x永久免费视频 | 91av福利视频| 激情黄色av | 人妻中文字幕一区 | 成人区人妻精品一熟女 | 少妇一夜三次一区二区 | 亚洲免费一级片 | 日朝毛片| 一区二区美女视频 | 91成年人视频| 欧洲av一区二区三区 | 中文字幕乱码一区 |