ThreadStatic属性如何工作?

时间:2011-03-08 02:34:27

标签: c# static threadstatic

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

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

更新

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

4 个答案:

答案 0 :(得分:107)

  

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

答案 1 :(得分:85)

在.NET jit编译器中,线程静态的实现语义低于IL级别。像VB.NET和C#一样向IL发出的编译器不需要知道任何关于Win32 TLS的内容,以便发出可以读取和写入具有ThreadStatic属性的变量的IL代码。就C#而言,变量并没有什么特别之处 - 它只是一个读写东西的位置。它具有属性的事实对C#没有影响。 C#只需要知道为该符号名称发出IL读或写指令。

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

这也可以解释为什么将属性放在不合适的(非静态)符号上不会得到编译器的反应。编译器不知道属性需要什么特殊语义。但是,像FX / Cop这样的代码分析工具应该知道它。

另一种查看方式:CIL定义了一组存储范围:静态(全局)存储,成员存储和堆栈存储。 TLS不在该列表中,很可能因为TLS不需要在该列表上。如果IL读取和写入指令足以在使用TLS属性标记符号时访问TLS,为什么IL对TLS有任何特殊表示或处理?这不是必需的。

答案 2 :(得分:4)

[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();
}

答案 3 :(得分:1)

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

TLS字段可通过gs / fs段寄存器进行访问.OS内核使用这些段来访问特定于线程的内存.net编译器不会发出任何IL来填充/获取TLS中的值。它是由OS内核完成的。