Wie funktioniert das ThreadStatic-Attribut?

138

Wie funktioniert [ThreadStatic]Attribut? Ich nahm an, dass der Compiler eine IL ausgeben würde, um den Wert im TLS zu stopfen / abzurufen, aber bei einer Demontage scheint dies auf dieser Ebene nicht der Fall zu sein.

Was passiert im Anschluss, wenn Sie es einem nicht statischen Mitglied zuweisen? Wir haben diesen Fehler von einem Entwickler machen lassen, und der Compiler gibt nicht einmal eine Warnung aus.

Aktualisieren

Zweite Frage hier beantwortet: ThreadStatic Modifiziert mit Static C #

Joshperry
quelle
1
Wenn die generierte IL dieselbe ist (was tatsächlich der Fall ist), muss die Laufzeit speziell codiert werden, um zu wissen, wie der Wert zugewiesen und gelesen wird, wenn er auf ein solches dekoriertes Feld trifft. Scheint wie ein Hack :)
Rex M

Antworten:

92

Die Implementierungssemantik von Thread Static liegt im .NET Jit Compiler unter der IL-Ebene. Compiler, die wie VB.NET und C # an IL emittieren, müssen nichts über Win32 TLS wissen, um IL-Code zu emittieren, der eine Variable mit dem ThreadStatic-Attribut lesen und schreiben kann. Soweit C # weiß, ist die Variable nichts Besonderes - sie ist nur ein Ort zum Lesen und Schreiben. Die Tatsache, dass es ein Attribut enthält, hat für C # keine Konsequenz. C # muss nur wissen, um IL-Lese- oder Schreibanweisungen für diesen Symbolnamen auszugeben.

Das "schwere Heben" wird von der Kern-CLR durchgeführt, die dafür verantwortlich ist, dass die IL auf einer bestimmten Hardwarearchitektur funktioniert.

Dies würde auch erklären, warum das Setzen des Attributs auf ein unangemessenes (nicht statisches) Symbol keine Reaktion vom Compiler erhält. Der Compiler weiß nicht, welche spezielle Semantik das Attribut erfordert. Code-Analyse-Tools wie FX / Cop sollten jedoch darüber Bescheid wissen.

Eine andere Sichtweise: CIL definiert eine Reihe von Speicherbereichen: statischer (globaler) Speicher, Mitgliedsspeicher und Stapelspeicher. TLS ist nicht auf dieser Liste, sehr wahrscheinlich, weil TLS nicht auf dieser Liste stehen muss. Wenn IL-Lese- und Schreibanweisungen ausreichen, um auf TLS zuzugreifen, wenn das Symbol mit einem TLS-Attribut gekennzeichnet ist, warum sollte IL eine spezielle Darstellung oder Behandlung für TLS haben? Es wird nicht benötigt.

dthorpe
quelle
Aber untergräbt dieses spezielle, implementierungsspezifische Verhalten von TLS nicht vollständig das "überprüfbare" Verkaufsargument von .NET / CLR?
Dai
116

Wie funktioniert das Attribut [ThreadStatic]?

Sie können sich vorstellen , dass das mit ThreadStatic markierte Feld an einen Thread angehängt ist und seine Lebensdauer mit der Lebensdauer eines Threads vergleichbar ist.

Im Pseudocode ThreadStaticist es also (semantisch) ähnlich, einen Schlüsselwert an einen Thread anzuhängen:

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

aber die Syntax ist nur ein bisschen einfacher:

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

Was passiert, wenn Sie es auf ein nicht statisches Mitglied legen?

Ich glaube, es wird ignoriert:

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

Erwähnenswert ist außerdem, dass ThreadStaticim Vergleich zu normalen statischen Feldern kein Synchronisationsmechanismus erforderlich ist (da der Status nicht gemeinsam genutzt wird).

Dmytrii Nagirniak
quelle
1
Zweitens sollte Pseudo-Code sein "MyClass.myVariable", nicht wahr?
Akshay2000
Ich bin mir der genauen Einschränkungen nicht sicher, aber ich wollte nur darauf hinweisen, wenn es nicht offensichtlich ist, dass es kein primitiver Typ sein muss. Wenn Sie sich die Quelle ansehen TransactionScope, speichern sie dort alle möglichen
Dinge
10

Der [ThreadStatic] erstellt in jedem Thread isolierte Versionen derselben Variablen.

Beispiel:

[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
quelle
3

Das mit gekennzeichnete Feld [ThreadStatic]wird im lokalen Thread-Speicher erstellt, sodass jeder Thread eine eigene Kopie des Felds hat, dh der Bereich der Felder ist lokal für den Thread.

TLS-Felder sind der Zugriff über gs / fs-Segmentregister. Diese Segmente werden von den Betriebssystemkernen verwendet, um auf threadspezifischen Speicher zuzugreifen. Der .net-Compiler gibt keine IL aus, um den Wert im TLS zu stopfen / abzurufen. Dies geschieht durch den Betriebssystemkern.

Arif H-Shigri
quelle