Come funziona l'attributo ThreadStatic?


138

Come funziona l' [ThreadStatic]attributo? Ho ipotizzato che il compilatore avrebbe emesso un po 'di IL per riempire / recuperare il valore nel TLS, ma guardando uno smontaggio non sembra farlo a quel livello.

Come follow-up, cosa succede se lo metti su un membro non statico? Abbiamo fatto commettere da uno sviluppatore quell'errore e il compilatore non ha nemmeno dato un avvertimento.

Aggiornare

La seconda domanda ha risposto qui: ThreadStatic modificato con C # statico


1
Se l'IL generato è lo stesso (che in effetti lo è), il runtime deve essere codificato in modo specifico per sapere come allocare e leggere il valore quando colpisce un campo così decorato. Sembra un trucco :)
Rex M

Risposte:


92

La semantica di implementazione di thread static è al di sotto del livello IL, nel compilatore jit .NET. I compilatori che emettono su IL come VB.NET e C # non hanno bisogno di sapere nulla su Win32 TLS per emettere codice IL in grado di leggere e scrivere una variabile con l'attributo ThreadStatic. Non c'è niente di speciale nella variabile per quanto ne sa C #: è solo un luogo in cui leggere e scrivere cose. Il fatto che abbia un attributo su di esso non ha alcuna conseguenza per C #. C # deve solo sapere di emettere IL leggere o scrivere istruzioni per quel nome di simbolo.

Il "sollevamento di carichi pesanti" viene eseguito dal CLR di base che è responsabile del funzionamento dell'IL su una particolare architettura hardware.

Ciò spiegherebbe anche perché l'inserimento dell'attributo su un simbolo inappropriato (non statico) non ottiene una reazione dal compilatore. Il compilatore non sa quale semantica speciale l'attributo richiede. Gli strumenti di analisi del codice come FX / Cop, tuttavia, dovrebbero saperlo.

Un altro modo per esaminarlo: CIL definisce un set di ambiti di archiviazione: archiviazione statica (globale), archiviazione dei membri e archiviazione dello stack. TLS non è in quella lista, molto probabilmente perché TLS non ha bisogno di essere in quella lista. Se le istruzioni di lettura e scrittura di IL sono sufficienti per accedere a TLS quando il simbolo è taggato con un attributo TLS, perché IL dovrebbe avere una rappresentazione o un trattamento speciale per TLS? Non è necessario.


Ma quel comportamento speciale e specifico dell'implementazione di TLS non sovverte completamente il punto di vendita "verificabile" di .NET / CLR?
Dai

116

Come funziona l'attributo [ThreadStatic]?

Puoi pensare che il campo contrassegnato con ThreadStatic sia collegato a un thread e la sua durata sia paragonabile alla durata di un thread.

Quindi in pseudocodice ThreadStaticè simile (per semantica) ad avere un valore-chiave collegato a un thread:

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

ma la sintassi è solo un po 'più semplice:

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

cosa succede se lo metti su un membro non statico?

Credo che sia ignorato:

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

Inoltre, vale la pena ricordare che ThreadStaticnon richiede alcun meccanismo di sincronizzazione rispetto ai normali campi statici (perché lo stato non è condiviso).


1
Il secondo tipo di pseudo codice dovrebbe essere "MyClass.myVariable", no?
akshay2000,

Non sono sicuro delle esatte restrizioni, ma volevo solo sottolineare se non è ovvio che non deve essere un tipo primitivo. Se guardi alla fonte TransactionScopeperché memorizzano ogni sorta di cose lì dentro per l'ambito ( riferimentiource.microsoft.com/#System.Transactions/System/… )
Simon_Weaver

10

[ThreadStatic] crea versioni isolate della stessa variabile in ciascun thread.

Esempio:

[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

I campi contrassegnati con [ThreadStatic]vengono creati su Archiviazione locale thread, quindi ogni thread ha la propria copia del campo, ovvero l'ambito dei campi è locale al thread.

I campi TLS sono accessibili tramite i registri dei segmenti gs / fs. Questi segmenti vengono utilizzati dai kernel del sistema operativo per accedere alla memoria specifica del thread. Il compilatore .net non emette alcun IL per inviare / recuperare il valore nel TLS. Viene eseguito dal kernel del sistema operativo.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.