Threadlokaler Speicher (TLS)

Durch den lokalen Threadspeicher (Thread Local Storage, TLS) kann jeder Thread in einem bestimmten Multithreadprozess Speicherplätze für threadspezifische Daten zuordnen. Dynamisch gebundene (Laufzeit)-threadspezifische Daten werden über die TLS-API (TlsAlloc) unterstützt. Win32 und der Microsoft C++-Compiler unterstützen jetzt zusätzlich zur vorhandenen API-Implementierung statisch gebundene (Ladezeit) pro Threaddaten.

Compilerimplementierung für TLS

C++11: Der thread_local Speicherklassenbezeichner ist die empfohlene Methode zum Angeben des threadlokalen Speichers für Objekte und Klassenmber. Weitere Informationen finden Sie unter Speicherklassen (C++).

MSVC stellt außerdem ein microsoftspezifisches Attribut, Thread, als Modifizierer für erweiterte Speicherklassen bereit. Verwenden Sie die __declspec Schlüsselwort (keyword), um eine thread Variable zu deklarieren. Mit folgendem Code wird z. B. eine Ganzzahl-TLS-Variable deklariert und mit einem Wert initialisiert:

__declspec( thread ) int tls_i = 1;

Regeln und Einschränkungen

Die folgenden Richtlinien müssen bei der Deklaration von statisch gebundenen lokalen Threadobjekten und -variablen beachtet werden: Diese Richtlinien gelten sowohl für Thread als auch für thread_local:

  • Das thread Attribut kann nur auf Klassen- und Datendeklarationen und Definitionen angewendet werden. Sie kann nicht für Funktionsdeklarationen oder Definitionen verwendet werden. Beispielsweise verursacht der folgende Code einen Compilerfehler:

    __declspec( thread )void func();     // This will generate an error.
    
  • Der thread Modifizierer kann nur für Datenelemente im static Umfang angegeben werden. Dazu gehören globale Datenobjekte (sowohl als externauch static ), lokale statische Objekte und statische Datenelemente von C++-Klassen. Automatische Datenobjekte können nicht mit dem thread Attribut deklariert werden. Im folgenden Code werden Compilerfehler generiert:

    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;
    }
    
  • Die Deklarationen und die Definition eines lokalen Threadobjekts müssen das thread Attribut angeben. Durch folgenden Code wird z. B. ein Fehler verursacht:

    #define Thread  __declspec( thread )
    extern int tls_i;        // This will generate an error, since the
    int __declspec( thread )tls_i;        // declaration and definition differ.
    
  • Das thread Attribut kann nicht als Typmodifizierer verwendet werden. Beispielsweise verursacht der folgende Code einen Compilerfehler:

    char __declspec( thread ) *ch;        // Error
    
  • Da die Deklaration von C++-Objekten, die das thread Attribut verwenden, zulässig ist, sind die folgenden beiden Beispiele semantisch gleichwertig:

    __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.
    
  • Die Adresse eines lokalen Threadobjekts wird nicht als Konstante betrachtet, und ein Ausdruck, der eine solche Adresse umfasst, wird nicht als konstanter Ausdruck betrachtet. In Standard C besteht der Effekt darin, die Verwendung der Adresse einer lokalen Threadvariable als Initialisierer für ein Objekt oder Zeiger zu verbieten. Folgender Code wird z. B. vom C-Compiler mit einem Fehlerflag versehen:

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

    Diese Einschränkung gilt nicht in C++. Da C++ die dynamische Initialisierung aller Objekte gestattet, kann ein Objekt mit einem Ausdruck initialisiert werden, der die Adresse einer threadlokalen Variablen verwendet. Dies geschieht genau wie beim Erstellen von lokalen Threadobjekten. Beispielsweise generiert der zuvor gezeigte Code keinen Fehler, wenn er als C++-Quelldatei kompiliert wird. Die Adresse einer lokalen Threadvariable ist nur gültig, solange der Thread, in dem die Adresse verwendet wurde, noch vorhanden ist.

  • Standard C ermöglicht die Initialisierung eines Objekts oder einer Variablen mit einem Ausdruck, der einen Verweis auf sich selbst umfasst, jedoch nur für Objekte, die nicht statisch sind. Obwohl C++ im Allgemeinen eine solche dynamische Initialisierung von Objekten mit einem Ausdruck zulässt, der einen Verweis auf sich selbst umfasst, ist diese Art von Initialisierung mit threadlokalen Objekten nicht zulässig. Beispiel:

    __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++
    

    Ein sizeof Ausdruck, der das initialisierte Objekt enthält, stellt keinen Verweis auf sich selbst dar und ist sowohl in C als auch in C++ aktiviert.

    C++ lässt eine solche dynamische Initialisierung von Threaddaten aufgrund möglicher zukünftiger Verbesserungen an der lokalen Threadspeichereinrichtung nicht zu.

  • Unter Windows-Betriebssystemen vor Windows Vista __declspec( thread ) gelten einige Einschränkungen. Wenn eine DLL Daten oder Objekte als __declspec( thread )deklariert, kann sie einen Schutzfehler verursachen, wenn dynamisch geladen. Nachdem die DLL mit LoadLibrary geladen wurde, verursacht sie Systemfehler, wenn der Code auf die __declspec( thread ) Daten verweist. Da der globale Variablenspeicher für einen Thread zur Laufzeit reserviert wird, basiert die Größe dieses Speichers auf der Berechnung der Anforderungen der jeweiligen Anwendung sowie der Anforderungen aller DLLs, die statisch gebunden sind. Wenn Sie diesen Bereich verwenden LoadLibrary, können Sie diesen Speicherplatz nicht erweitern, um die lokalen Threadvariablen zuzulassen, die mit __declspec( thread ). Verwenden Sie die TLS-APIs, z . B. TlsAlloc, in Ihrer DLL, um TLS zuzuweisen, wenn die DLL möglicherweise mit LoadLibrarygeladen wird.

Siehe auch

Multithreading bei C und Win32