Trådlokal lagring (TLS)

TLS (Thread Local Storage) är den metod med vilken varje tråd i en viss process med flera trådar kan allokera platser där trådspecifika data ska lagras. Trådspecifika data som är dynamiskt bundna (körningstid) stöds via TLS API (TlsAlloc). Win32 och Microsoft C++-kompilatorn stöder nu statiskt bundna data (belastningstid) per tråd utöver den befintliga API-implementeringen.

Implementering av kompilator för TLS

C++11: Lagringsklassspecificeraren thread_local är det rekommenderade sättet att ange trådlokal lagring för objekt och klassmedlemmar. Mer information finns i Lagringsklasser (C++).

MSVC tillhandahåller också ett Microsoft-specifikt attribut, tråd, som utökad lagringsklassmodifierare. Använd nyckelordet __declspec för att deklarera en thread variabel. Följande kod deklarerar till exempel en lokal heltalstrådsvariabel och initierar den med ett värde:

__declspec( thread ) int tls_i = 1;

Regler och begränsningar

Följande riktlinjer måste följas när statiskt bundna trådlokala objekt och variabler deklareras. Dessa riktlinjer gäller både för tråd och för thread_local:

  • Attributet thread kan endast tillämpas på klass- och datadeklarationer och definitioner. Det kan inte användas i funktionsdeklarationer eller definitioner. Följande kod genererar till exempel ett kompilatorfel:

    __declspec( thread )void func();     // This will generate an error.
    
  • Modifieraren thread kan endast anges för dataobjekt i static viss utsträckning. Det inkluderar globala dataobjekt (både static och extern), lokala statiska objekt och statiska datamedlemmar i C++-klasser. Automatiska dataobjekt kan inte deklareras med attributet thread . Följande kod genererar kompilatorfel:

    void func1()
    {
        __declspec( thread )int tls_i;            // This will generate an error.
    }
    
    int func2(__declspec( thread )int tls_i )     // This will generate an error.
    {
        return tls_i;
    }
    
  • Deklarationerna och definitionen av ett lokalt trådobjekt måste alla ange attributet thread . Följande kod genererar till exempel ett fel:

    #define Thread  __declspec( thread )
    extern int tls_i;        // This will generate an error, since the
    int __declspec( thread )tls_i;        // declaration and definition differ.
    
  • Attributet thread kan inte användas som typmodifierare. Följande kod genererar till exempel ett kompilatorfel:

    char __declspec( thread ) *ch;        // Error
    
  • Eftersom deklarationen av C++-objekt som använder thread attributet tillåts är följande två exempel semantiskt likvärdiga:

    __declspec( thread ) class B
    {
    // Code
    } BObject;  // OK--BObject is declared thread local.
    
    class B
    {
    // Code
    };
    __declspec( thread ) B BObject;  // OK--BObject is declared thread local.
    
  • Adressen till ett lokalt trådobjekt betraktas inte som konstant och alla uttryck som involverar en sådan adress betraktas inte som ett konstant uttryck. I standard C är effekten att inte tillåta att adressen för en trådlokal variabel används som initiator för ett objekt eller en pekare. Följande kod flaggas till exempel som ett fel av C-kompilatorn:

    __declspec( thread ) int tls_i;
    int *p = &tls_i;       //This will generate an error in C.
    

    Den här begränsningen gäller inte i C++. Eftersom C++ möjliggör dynamisk initiering av alla objekt kan du initiera ett objekt med hjälp av ett uttryck som använder adressen för en trådlokal variabel. Det görs precis som skapandet av trådlokala objekt. Koden som visades tidigare genererar till exempel inget fel när den kompileras som en C++-källfil. Adressen för en lokal trådvariabel är endast giltig så länge tråden där adressen togs fortfarande finns.

  • Standard C möjliggör initiering av ett objekt eller en variabel med ett uttryck som omfattar en referens till sig själv, men endast för objekt av icke-statisk omfattning. Även om C++ i allmänhet tillåter sådan dynamisk initiering av objekt med ett uttryck som innehåller en referens till sig själv, tillåts inte den här typen av initiering med trådlokala objekt. Till exempel:

    __declspec( thread )int tls_i = tls_i;                // Error in C and C++
    int j = j;                               // OK in C++, error in C
    __declspec( thread )int tls_i = sizeof( tls_i )       // Legal in C and C++
    

    Ett sizeof uttryck som innehåller objektet som initieras representerar inte en referens till sig själv och är aktiverat i både C och C++.

    C++ tillåter inte sådan dynamisk initiering av tråddata på grund av eventuella framtida förbättringar av den lokala trådlagringsanläggningen.

  • På Windows-operativsystem före Windows Vista __declspec( thread ) har vissa begränsningar. Om en DLL deklarerar data eller objekt som __declspec( thread )kan det orsaka ett skyddsfel om de läses in dynamiskt. När DLL:en har lästs in med LoadLibrary orsakar den systemfel när koden refererar till __declspec( thread ) data. Eftersom det globala variabelutrymmet för en tråd allokeras vid körning baseras storleken på det här utrymmet på en beräkning av programmets krav plus kraven för alla DLL:er som är statiskt länkade. När du använder LoadLibrarykan du inte utöka det här utrymmet för att tillåta de lokala trådvariabler som deklarerats med __declspec( thread ). Använd TLS-API:erna, till exempel TlsAlloc, i din DLL för att allokera TLS om DLL-filen kan läsas in med LoadLibrary.

Se även

Multitrådning med C och Win32