Jak działa atrybut ThreadStatic?

138

Jak działa [ThreadStatic]atrybut? Założyłem, że kompilator wyemituje trochę IL, aby załadować / pobrać wartość w TLS, ale patrząc na dezasemblację, wydaje się, że nie robi tego na tym poziomie.

W dalszej kolejności, co się stanie, jeśli umieścisz go na niestatycznym elemencie? Deweloper popełnił ten błąd, a kompilator nawet nie daje ostrzeżenia.

Aktualizacja

Odpowiedź na drugie pytanie: ThreadStatic zmodyfikowana za pomocą statycznego C #

joshperry
źródło
1
Jeśli wygenerowany IL jest taki sam (a rzeczywiście tak jest), to środowisko wykonawcze musi być specjalnie zakodowane, aby wiedzieć, jak przydzielić i odczytać wartość, gdy trafi ona w takie ozdobione pole. Wygląda na włamanie :)
Rex M

Odpowiedzi:

92

Semantyka implementacji wątku statycznego jest poniżej poziomu IL, w kompilatorze jit .NET. Kompilatory, które emitują do IL, takie jak VB.NET i C #, nie muszą nic wiedzieć o Win32 TLS, aby emitować kod IL, który może odczytywać i zapisywać zmienną, która ma atrybut ThreadStatic. O ile C # wie, nie ma nic specjalnego w tej zmiennej - jest to tylko lokalizacja do czytania i pisania. Fakt, że ma przypisany atrybut, nie ma znaczenia dla C #. C # musi tylko wiedzieć, aby emitować instrukcje odczytu lub zapisu IL dla tej nazwy symbolu.

„Podnoszenie ciężarów” jest wykonywane przez rdzeń środowiska CLR, które jest odpowiedzialne za sprawienie, by IL działał na określonej architekturze sprzętowej.

To by również wyjaśniało, dlaczego umieszczenie atrybutu na niewłaściwym (niestatycznym) symbolu nie powoduje reakcji kompilatora. Kompilator nie wie, jakiej specjalnej semantyki wymaga atrybut. Jednak narzędzia do analizy kodu, takie jak FX / Cop, powinny o tym wiedzieć.

Można na to spojrzeć inaczej: CIL definiuje zestaw zakresów pamięci: pamięć statyczną (globalną), pamięć składową i pamięć stosową. TLS nie znajduje się na tej liście, bardzo prawdopodobne, ponieważ TLS nie musi znajdować się na tej liście. Jeśli instrukcje odczytu i zapisu IL są wystarczające, aby uzyskać dostęp do TLS, gdy symbol jest oznaczony atrybutem TLS, dlaczego IL miałby mieć jakąś specjalną reprezentację lub sposób traktowania TLS? To nie jest potrzebne.

dthorpe
źródło
Ale czy to specjalne, specyficzne dla implementacji zachowanie TLS nie podważa całkowicie „weryfikowalnego” punktu sprzedaży .NET / CLR?
Dai
116

Jak działa atrybut [ThreadStatic]?

Możesz pomyśleć, że pole oznaczone ThreadStatic jest dołączone do wątku, a jego żywotność jest porównywalna z czasem życia wątku.

Więc w pseudokodzie ThreadStaticjest podobne (semantycznie) do posiadania klucza-wartości dołączonej do wątku:

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

ale składnia jest trochę łatwiejsza:

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

co się stanie, jeśli umieścisz go na niestatycznym elemencie?

Uważam, że jest to ignorowane:

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

Dodatkowo warto wspomnieć, że ThreadStaticnie wymaga żadnego mechanizmu synchronizacji w porównaniu do zwykłych pól statycznych (ponieważ stan nie jest współdzielony).

Dmytrii Nagirniak
źródło
1
Powinno być drugim podobnym do pseudokodu "MyClass.myVariable", prawda?
akshay2000
Nie jestem pewien dokładnych ograniczeń, ale chciałem tylko wskazać, czy nie jest oczywiste, że nie musi to być typ prymitywny. Jeśli spojrzysz na źródło, TransactionScopeponieważ przechowują tam różnego rodzaju rzeczy dla zakresu ( referenceource.microsoft.com/#System.Transactions/System/… )
Simon_Weaver
10

[ThreadStatic] tworzy izolowane wersje tej samej zmiennej w każdym wątku.

Przykład:

[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
źródło
3

Pola oznaczone [ThreadStatic]są tworzone w Thread Local Storage, więc każdy wątek ma swoją własną kopię pola, tj. Zakres pól jest lokalny dla wątku.

Dostęp do pól TLS uzyskuje się przez rejestry segmentów gs / fs. Te segmenty są używane przez jądra systemu operacyjnego do uzyskiwania dostępu do pamięci specyficznej dla wątków. Robi to jądro systemu operacyjnego.

Arif H-Shigri
źródło