150

[ThreadStatic]属性如何工作?我假设编译器会发出一些 IL 来填充/检索 TLS 中的值,但是查看反汇编似乎并没有在该级别执行此操作。

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

更新

第二个问题在这里回答:ThreadStatic Modified with Static C#

4

4 回答 4

123

[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,与普通静态字段相比,它不需要任何同步机制(因为不共享状态)。

于 2011-03-08T02:48:27.360 回答
100

在 .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 有任何特殊表示或处理?这不是必需的。

于 2011-03-08T02:51:48.410 回答
17

[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();
}
于 2018-04-19T13:18:49.033 回答
9

标记 的字段[ThreadStatic]是在线程本地存储上创建的,因此每个线程都有自己的字段副本,即字段的范围是线程本地的。

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

于 2019-10-21T19:06:05.090 回答