Comment fonctionne l'attribut ThreadStatic?

138

Comment fonctionne l' [ThreadStatic]attribut? J'ai supposé que le compilateur émettrait de l'IL pour bourrer / récupérer la valeur dans le TLS, mais en regardant un démontage, il ne semble pas le faire à ce niveau.

En guise de suivi, que se passe-t-il si vous le mettez sur un membre non statique? Un développeur a fait cette erreur et le compilateur n'a même pas émis d'avertissement.

Mettre à jour

Deuxième question répondue ici: ThreadStatic modifié avec C # statique

Joshperry
la source
1
Si l'IL généré est le même (ce qui est en fait le cas), alors le runtime doit être codé spécifiquement pour savoir comment allouer et lire la valeur quand elle atteint un tel champ décoré. On dirait un hack :)
Rex M

Réponses:

92

La sémantique d'implémentation de thread static est inférieure au niveau IL, dans le compilateur .NET jit. Les compilateurs qui émettent vers IL comme VB.NET et C # n'ont pas besoin de savoir quoi que ce soit sur Win32 TLS pour émettre du code IL capable de lire et d'écrire une variable avec l'attribut ThreadStatic. Il n'y a rien de spécial à propos de la variable pour autant que C # sache - c'est juste un emplacement pour lire et écrire des choses. Le fait qu'il ait un attribut dessus n'a aucune conséquence pour C #. C # a seulement besoin de savoir pour émettre des instructions de lecture ou d'écriture IL pour ce nom de symbole.

Le «gros travail» est effectué par le CLR principal qui est chargé de faire fonctionner l'IL sur une architecture matérielle particulière.

Cela expliquerait également pourquoi placer l'attribut sur un symbole inapproprié (non statique) n'obtient pas une réaction du compilateur. Le compilateur ne sait pas de quelle sémantique spéciale l'attribut a besoin. Les outils d'analyse de code comme FX / Cop, cependant, devraient le savoir.

Une autre façon de voir les choses: CIL définit un ensemble d'étendues de stockage: stockage statique (global), stockage membre et stockage de pile. TLS n'est pas sur cette liste, très probablement parce que TLS n'a pas besoin d'être sur cette liste. Si les instructions de lecture et d'écriture IL sont suffisantes pour accéder à TLS lorsque le symbole est étiqueté avec un attribut TLS, pourquoi IL devrait-il avoir une représentation ou un traitement spécial pour TLS? Ce n'est pas nécessaire.

dthorpe
la source
Mais ce comportement spécial et spécifique à l'implémentation de TLS ne détruit-il pas complètement le point de vente «vérifiable» de .NET / CLR?
Dai
116

Comment l'attribut [ThreadStatic] fonctionne-t-il?

Vous pouvez penser que le champ marqué avec ThreadStatic est attaché à un thread et sa durée de vie est comparable à la durée de vie d'un thread.

Donc, dans le pseudocode, ThreadStaticc'est similaire (par sémantique) à avoir une valeur-clé attachée à un thread:

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

mais la syntaxe est juste un peu plus simple:

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

que se passe-t-il si vous le mettez sur un membre non statique?

Je pense qu'il est ignoré:

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

De plus, il convient de mentionner qu'il ThreadStaticne nécessite aucun mécanisme de synchronisation par rapport aux champs statiques normaux (car l'état n'est pas partagé).

Dmytrii Nagirniak
la source
1
Le deuxième type de pseudo-code devrait être "MyClass.myVariable", n'est-ce pas?
akshay2000
Je ne suis pas sûr des restrictions exactes, mais je voulais juste souligner s'il n'est pas évident qu'il ne doit pas être de type primitif. Si vous regardez la source car TransactionScopeils stockent toutes sortes de choses là-dedans pour la portée ( referencesource.microsoft.com/#System.Transactions/System/… )
Simon_Weaver
10

[ThreadStatic] crée des versions isolées de la même variable dans chaque thread.

Exemple:

[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();
}
Rui Ruivo
la source
3

Les champs marqués avec [ThreadStatic]sont créés sur Thread Local Storage afin que chaque thread ait sa propre copie du champ, c'est-à-dire que la portée des champs est locale au thread.

Les champs TLS sont accessibles via les registres de segments gs / fs. Ces segments sont utilisés par les noyaux du système d'exploitation pour accéder à la mémoire spécifique aux threads. Le compilateur .net n'émet aucun IL pour bourrer / récupérer la valeur dans le TLS. Cela est fait par le noyau du système d'exploitation.

Arif H-Shigri
la source