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 imstatic
Umfang angegeben werden. Dazu gehören globale Datenobjekte (sowohl alsextern
auchstatic
), lokale statische Objekte und statische Datenelemente von C++-Klassen. Automatische Datenobjekte können nicht mit demthread
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 verwendenLoadLibrary
, 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 mitLoadLibrary
geladen wird.