ThreadStatic 属性是如何工作的?

[ThreadStatic]属性如何工作?我假设编译器会发出一些 IL 来填充/检索 TLS 中的值,但是看看反汇编,它似乎没有在那个级别上做到这一点。

作为后续工作,如果把它放在一个非静态成员上会发生什么?我们有一个开发人员犯了这个错误,而编译器甚至没有提供一个警告。

更新

这里回答了第二个问题: 用静态 C # 修改 ThreadStatic

68793 次浏览

[ 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不需要任何同步机制(因为状态不是共享的)。

线程静态的实现语义低于 IL 级别,在。NET jit 编译器。向 IL 发出的编译器,如 VB.NET 和 C # ,不需要知道任何关于 Win32 TLS 的信息,就可以发出可以读写具有 ThreadStatic 属性的变量的 IL 代码。就 C # 所知,这个变量并没有什么特别之处——它只是一个读写东西的位置。事实上,它上面有一个属性对 C # 来说是无关紧要的。C # 只需要知道发出该符号名的 IL 读或写指令。

“繁重的工作”是由核心 CLR 完成的,它负责让 IL 在特定的硬件架构上工作。

这也可以解释为什么将属性放在不合适的(非静态)符号上不会得到编译器的反应。编译器不知道属性需要什么特殊的语义。不过,像 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();
}

The field marked with [ThreadStatic] are created on Thread Local Storage so every thread has it own copy of the field i.e the scope of the fields are local to the thread.

TLS 字段是通过 gs/fs 段寄存器访问的。操作系统内核使用这些段来访问特定于线程的内存。那个。Net 编译器不会发出任何 IL 来填充/检索 TLS 中的值。它是由操作系统内核完成的。