¿Cómo funciona el atributo ThreadStatic?


138

¿Cómo funciona el [ThreadStatic]atributo? Supuse que el compilador emitiría algo de IL para rellenar / recuperar el valor en el TLS, pero mirando un desmontaje no parece hacerlo a ese nivel.

Como seguimiento, ¿qué sucede si lo pones en un miembro no estático? Hicimos que un desarrollador cometiera ese error y el compilador ni siquiera ofrece una advertencia.

Actualizar

Segunda pregunta respondida aquí: ThreadStatic modificado con Static C #


1
Si el IL generado es el mismo (que de hecho lo es), entonces el tiempo de ejecución debe codificarse específicamente para saber cómo asignar y leer el valor cuando llega a un campo tan decorado. Parece un truco :)
Rex M

Respuestas:


92

La semántica de implementación de hilo estático está por debajo del nivel IL, en el compilador .NET jit. Los compiladores que emiten a IL como VB.NET y C # no necesitan saber nada sobre Win32 TLS para emitir código IL que pueda leer y escribir una variable que tenga el atributo ThreadStatic. No hay nada especial en la variable hasta donde C # sabe: es solo una ubicación para leer y escribir cosas. El hecho de que tenga un atributo no tiene consecuencias para C #. C # solo necesita saber para emitir IL leer o escribir instrucciones para ese nombre de símbolo.

El 'levantamiento pesado' lo realiza el CLR central que es responsable de hacer que el IL funcione en una arquitectura de hardware particular.

Eso también explicaría por qué poner el atributo en un símbolo inapropiado (no estático) no obtiene una reacción del compilador. El compilador no sabe qué semántica especial requiere el atributo. Sin embargo, las herramientas de análisis de código como FX / Cop deberían saberlo.

Otra forma de verlo: CIL define un conjunto de ámbitos de almacenamiento: almacenamiento estático (global), almacenamiento de miembros y almacenamiento de pila. TLS no está en esa lista, muy probablemente porque TLS no necesita estar en esa lista. Si las instrucciones de lectura y escritura de IL son suficientes para acceder a TLS cuando el símbolo está etiquetado con un atributo TLS, ¿por qué IL debería tener alguna representación o tratamiento especial para TLS? No es necesario


Pero, ¿ese comportamiento especial y específico de implementación de TLS no subvierte por completo el punto de venta "verificable" de .NET / CLR?
Dai

116

¿Cómo funciona el atributo [ThreadStatic]?

Puede pensar que el campo marcado con ThreadStatic está conectado a un subproceso y su duración es comparable a la duración de un subproceso.

Entonces, en pseudocódigo ThreadStatices similar (por semántica) a tener un valor clave adjunto a un hilo:

Thread.Current["MyClass.myVariable"] = 1;
Thread.Current["MyClass.myvariable"] += 1;

pero la sintaxis es un poco más fácil:

class MyClass {
  [ThreadStatic]
  static int myVariable;
}
// .. then
MyClass.myVariable = 1;
MyClass.myVariable += 1;

¿Qué sucede si lo pones en un miembro no estático?

Creo que se ignora:

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

Además, vale la pena mencionar que ThreadStaticno requiere ningún mecanismo de sincronización en comparación con los campos estáticos normales (porque el estado no se comparte).


1
El segundo tipo de pseudocódigo debería ser "MyClass.myVariable", ¿no?
akshay2000

No estoy seguro de las restricciones exactas, pero solo quería señalar si no es obvio que no tiene que ser un tipo primitivo. Si miras la fuente TransactionScope, almacenan todo tipo de cosas allí para el alcance ( referencesource.microsoft.com/#System.Transactions/System/… )
Simon_Weaver

10

[ThreadStatic] crea versiones aisladas de la misma variable en cada hilo.

Ejemplo:

[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

El campo marcado con [ThreadStatic] se crean en el Almacenamiento local de subprocesos, por lo que cada subproceso tiene su propia copia del campo, es decir, el alcance de los campos es local para el subproceso.

Los campos TLS son de acceso a través de registros de segmentos gs / fs. Estos segmentos son utilizados por los núcleos del sistema operativo para acceder a la memoria específica de subprocesos. El compilador .net no emite ningún IL para rellenar / recuperar el valor en el TLS. Lo hace el núcleo del sistema operativo.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.