C# 线程本地存储为什么线程间值不一样( 二 )

上面这段代码的步骤很清楚 。

  • 创建 ThreadLocalModule
  • 初始化 MethodTable 类型的字段 pMT
这个 pMT 非常重要,训练营里的朋友都知道 MethodTable 是 C# 的 class 承载,言外之意就是判断下这个 class 有没有被初始化,如果没有初始化那就调 静态构造函数,接下来的问题是 class 到底是哪一个类呢?
结合刚才汇编中的 mov edx,4 以及源码发现是取 IL 元数据中的 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;}
C# 线程本地存储为什么线程间值不一样

文章插图
图片
也可以用 windbg 在 JIT_GetNonGCThreadStaticBase_Helper 方法的 return 处下一个断点,参考如下:
0:008> r ecxecx=0564ef280:008> !dumpmt 0564ef28EEClass:056d14d0Module:0564db08Name:ConsoleApp7.ProgrammdToken:02000005File:D:codeMyApplicationConsoleApp7binx86Debug.NET6.0ConsoleApp7.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 。
3. 如何复验你的结论刚才我说 thread1 做了一个是否执行静态构造的判断 , 其实这里我可以做个手脚,在 Main 之前先把 Program 静态函数给执行掉,按理说 thread1 和 thread2 此时都会是默认值 num=0 , 对不对,哈哈,试一试呗,简化代码如下:
internal class Program{[ThreadStatic]public static int num = 10;/// <summary>/// 先于 main 执行/// </summary>static Program(){}static void Main(string[] args){Test();Console.ReadLine();}}
C# 线程本地存储为什么线程间值不一样

文章插图
图片
哈哈 , 此时都是 0 了,也就再次验证了我的结论 。
三:总结在 C# 开发中经常会有一些疑惑,如果不了解汇编 , C++ ,相信你会陷入到很多的魔法使用中而苦于不能独自解惑的遗憾 。

【C# 线程本地存储为什么线程间值不一样】


推荐阅读