[ThreadStatic]
属性如何工作?我假设编译器会发出一些 IL 来填充/检索 TLS 中的值,但是查看反汇编似乎并没有在该级别执行此操作。
作为后续,如果你把它放在一个非静态成员上会发生什么?我们让开发人员犯了这个错误,编译器甚至没有提供警告。
更新
第二个问题在这里回答:ThreadStatic Modified with Static C#
[ThreadStatic]
属性如何工作?我假设编译器会发出一些 IL 来填充/检索 TLS 中的值,但是查看反汇编似乎并没有在该级别执行此操作。
作为后续,如果你把它放在一个非静态成员上会发生什么?我们让开发人员犯了这个错误,编译器甚至没有提供警告。
更新
第二个问题在这里回答:ThreadStatic Modified with Static C#
[ThreadStatic] 属性如何工作?
你可以认为标有ThreadStatic的字段是附加到一个线程上的,它的生命周期相当于一个线程的生命周期。
因此,在伪代码ThreadStatic
中(通过语义)类似于将键值附加到线程:
Thread.Current["MyClass.myVariable"] = 1;
Thread.Current["MyClass.myVariable"] += 1;
但语法更简单一点:
class MyClass {
[ThreadStatic]
static int myVariable;
}
// .. then
MyClass.myVariable = 1;
MyClass.myVariable += 1;
如果你把它放在一个非静态成员上会发生什么?
我相信它被忽略了:
class A {
[ThreadStatic]
public int a;
}
[Test]
public void Try() {
var a1 = new A();
var a2 = new A();
a1.a = 5;
a2.a = 10;
a1.a.Should().Be.EqualTo(5);
a2.a.Should().Be.EqualTo(10);
}
另外值得一提的是ThreadStatic
,与普通静态字段相比,它不需要任何同步机制(因为不共享状态)。
在 .NET jit 编译器中,线程静态的实现语义低于 IL 级别。向 IL 发出的编译器(如 VB.NET 和 C#)不需要了解有关 Win32 TLS 的任何信息,即可发出可以读取和写入具有 ThreadStatic 属性的变量的 IL 代码。就 C# 所知,该变量没有什么特别之处——它只是一个读取和写入内容的位置。它具有属性这一事实对 C# 没有影响。C# 只需要知道为该符号名称发出 IL 读取或写入指令。
“繁重的工作”由负责使 IL 在特定硬件架构上工作的核心 CLR 完成。
这也可以解释为什么将属性放在不适当的(非静态)符号上不会得到编译器的反应。编译器不知道该属性需要什么特殊语义。不过,像 FX/Cop 这样的代码分析工具应该知道这一点。
另一种看待它的方式:CIL 定义了一组存储范围:静态(全局)存储、成员存储和堆栈存储。TLS 不在该列表中,很可能是因为 TLS 不需要在该列表中。如果当符号带有 TLS 属性标记时,IL 读写指令足以访问 TLS,为什么 IL 应该对 TLS 有任何特殊表示或处理?这不是必需的。
[ThreadStatic] 在每个线程中创建相同变量的隔离版本。
例子:
[ThreadStatic] public static int i; // Declaration of the variable i with ThreadStatic Attribute.
public static void Main()
{
new Thread(() =>
{
for (int x = 0; x < 10; x++)
{
i++;
Console.WriteLine("Thread A: {0}", i); // Uses one instance of the i variable.
}
}).Start();
new Thread(() =>
{
for (int x = 0; x < 10; x++)
{
i++;
Console.WriteLine("Thread B: {0}", i); // Uses another instance of the i variable.
}
}).Start();
}
标记 的字段[ThreadStatic]
是在线程本地存储上创建的,因此每个线程都有自己的字段副本,即字段的范围是线程本地的。
TLS 字段是通过 gs/fs 段寄存器访问的。OS 内核使用这些段来访问线程特定的内存。.net 编译器不会发出任何 IL 来填充/检索 TLS 中的值。它由操作系统内核完成。