Stockage local des threads (TLS)
Le stockage local des threads (TLS, \{i\>Thread Local Storage\<i\}) est une méthode où chaque thread d'un processus multithread donné peut allouer des emplacements de stockage de données propres au thread. Les données spécifiques aux threads liées dynamiquement (au moment de l’exécution) sont prises en charge par le biais de l’API TLS (TlsAlloc). Win32 et le compilateur Microsoft C++ prennent désormais en charge les données par thread liées statiquement (temps de chargement) en plus de l’implémentation d’API existante.
Implémentation du compilateur pour TLS
C++11 : Le thread_local
spécificateur de classe de stockage est la méthode recommandée pour spécifier le stockage local thread pour les objets et les membres de classe. Pour plus d’informations, consultez Stockage classes (C++).
MSVC fournit également un attribut spécifique à Microsoft, thread, en tant que modificateur de classe de stockage étendu. Utilisez la __declspec
mot clé pour déclarer une thread
variable. Par exemple, le code suivant déclare une variable locale de thread entière et lui affecte une valeur initiale :
__declspec( thread ) int tls_i = 1;
Règles et limitations
Les instructions suivantes doivent être observées au moment de déclarer des variables et des objets locaux de thread liés statiquement. Ces instructions s’appliquent à la fois au thread et à thread_local :
L’attribut
thread
ne peut être appliqué qu’à la classe et aux déclarations et définitions de données. Il ne peut pas être utilisé sur les déclarations de fonction ou les définitions. Par exemple, le code suivant génère une erreur de compilation :__declspec( thread )void func(); // This will generate an error.
Le
thread
modificateur ne peut être spécifié que sur les éléments de données dansstatic
l’étendue. Cela inclut des objets de données globaux (à la foisstatic
etextern
), des objets statiques locaux et des membres de données statiques de classes C++. Les objets de données automatiques ne peuvent pas être déclarés avec l’attributthread
. Le code suivant génère des erreurs de compilation :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; }
Les déclarations et la définition d’un objet local de thread doivent tous spécifier l’attribut
thread
. Par exemple, le code suivant génère une erreur :#define Thread __declspec( thread ) extern int tls_i; // This will generate an error, since the int __declspec( thread )tls_i; // declaration and definition differ.
L’attribut
thread
ne peut pas être utilisé comme modificateur de type. Par exemple, le code suivant génère une erreur de compilation :char __declspec( thread ) *ch; // Error
Étant donné que la déclaration d’objets C++ qui utilisent l’attribut
thread
est autorisée, les deux exemples suivants sont sémantiquement équivalents :__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.
L’adresse d’un objet local de thread n’est pas considérée comme constante, et toute expression impliquant une telle adresse n’est pas considérée comme une expression constante. En C standard, l’effet consiste à interdire l’utilisation de l’adresse d’une variable locale de thread comme initialiseur pour un objet ou un pointeur. Par exemple, le code suivant est signalé comme une erreur par le compilateur C :
__declspec( thread ) int tls_i; int *p = &tls_i; //This will generate an error in C.
Cette restriction ne s’applique pas en C++. Sachant que C++ prévoit l’initialisation dynamique de tous les objets, vous pouvez initialiser un objet à l’aide d’une expression qui utilise l’adresse d’une variable locale de thread. Elle s’effectue tout comme la construction d’objets locaux de thread. Par exemple, le code présenté précédemment ne génère pas d’erreur lorsqu’il est compilé en tant que fichier source C++. L’adresse d’une variable locale de thread est valide uniquement tant que le thread dans lequel l’adresse a été prise existe toujours.
La norme C permet l’initialisation d’un objet ou d’une variable avec une expression qui implique une référence à elle-même, mais uniquement pour les objets d’étendue non statique. Bien que C++ autorise généralement une telle initialisation dynamique d’objets avec une expression qui implique une référence à elle-même, ce type d’initialisation n’est pas autorisé avec des objets locaux de thread. Par exemple :
__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++
Une
sizeof
expression qui inclut l’objet initialisé ne représente pas une référence à elle-même et est activée en C et C++.C++ n’autorise pas l’initialisation dynamique des données de thread en raison des améliorations possibles apportées à l’installation de stockage local du thread.
Sur les systèmes d’exploitation Windows avant Windows Vista,
__declspec( thread )
présente certaines limitations. Si une DLL déclare des données ou des objets en tant que__declspec( thread )
, elle peut provoquer une erreur de protection en cas de chargement dynamique. Une fois la DLL chargée avec LoadLibrary, elle provoque une défaillance système chaque fois que le code référence les__declspec( thread )
données. Sachant que l'espace de variables globales d'un thread est alloué au moment de l'exécution, la taille de cet espace varie en fonction du calcul des besoins de l'application et de ceux de toutes les DLL liées statiquement. Lorsque vous utilisezLoadLibrary
, vous ne pouvez pas étendre cet espace pour autoriser les variables locales de thread déclarées avec__declspec( thread )
. Utilisez les API TLS, telles que TlsAlloc, dans votre DLL pour allouer TLS si la DLL peut être chargée avecLoadLibrary
.