Règles et limitations pour TLS
Mise à jour : novembre 2007
Les instructions suivantes doivent être observées lors de la déclaration de variables et d'objets locaux statiques du thread :
L'attribut thread s'applique uniquement aux déclarations et définitions de données. Il ne peut pas être utilisé avec des définitions ou des déclarations de fonction. Par exemple, le code suivant génère une erreur de compilation :
#define Thread __declspec( thread ) Thread void func(); // This will generate an error.
Le modificateur thread peut être spécifié uniquement pour les éléments de données avec l'étendue static. Cela comprend les objets de données globaux (à la fois static et extern), les objets statiques locaux et les membres de données statiques des classes C++. Les objets de données automatiques ne peuvent pas être déclarés à l'aide de l'attribut thread. Le code suivant génère des erreurs de compilation :
#define Thread __declspec( thread ) void func1() { Thread int tls_i; // This will generate an error. } int func2( 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 toutes 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 Thread tls_i; // declaration and definition differ.
L'attribut thread ne peut pas servir de modificateur de type. Par exemple, le code suivant génère une erreur de compilation :
char __declspec( thread ) *ch; // Error
Les classes C++ ne peuvent pas utiliser l'attribut thread. Cependant, les objets des classes C++ peuvent être instanciés à l'aide de l'attribut thread. Par exemple, le code suivant génère une erreur de compilation :
#define Thread __declspec( thread ) class Thread C // Error: classes cannot be declared Thread. { // Code }; C CObject;
Dans la mesure où la déclaration d'objets C++ utilisant l'attribut thread est autorisée, les deux exemples suivants sont équivalents sur le plan sémantique :
#define Thread __declspec( thread ) Thread class B { // Code } BObject; // OK--BObject is declared thread local. class B { // Code }; Thread B BObject; // OK--BObject is declared thread local.
L'adresse de l'objet local d'un thread n'est pas considérée comme étant constante, et toute expression impliquant une telle adresse n'est pas considérée comme une expression constante. En langage C standard, ceci a pour effet d'interdire l'utilisation de l'adresse d'une variable locale de thread en tant qu'initialiseur d'un objet ou d'un pointeur. Par exemple, le code suivant est signalé en tant qu'erreur par le compilateur C :
#define Thread __declspec( thread ) Thread int tls_i; int *p = &tls_i; //This will generate an error in C.
Cette restriction ne s'applique pas en langage C++, cependant. Comme C++ autorise l'initialisation dynamique de tous les objets, vous pouvez initialiser un objet à l'aide d'une expression utilisant l'adresse d'une variable locale de thread. Cette opération est réalisée de la même façon que la construction d'objets locaux de thread. Par exemple, le code ci-dessus ne génère pas d'erreur lorsqu'il est compilé en tant que fichier source C++. Notez que la variable locale d'un thread est valide uniquement si le thread où l'adresse a été prise continue d'exister.
Le langage C standard permet l'initialisation d'un objet ou d'une variable à l'aide d'une expression impliquant une auto-référence, mais uniquement pour les objets d'étendue non statique. Même si C++ admet généralement l'initialisation dynamique des objets à l'aide d'une référence impliquant une auto-référence, ce type d'initialisation n'est pas permis avec les objets locaux de thread. Par exemple :
#define Thread __declspec( thread ) Thread int tls_i = tls_i; // Error in C and C++ int j = j; // OK in C++, error in C Thread int tls_i = sizeof( tls_i ) // Legal in C and C++
Notez qu'une expression sizeof comprenant l'objet initialisé ne constitue pas une auto-référence, et est admise aussi bien dans C que dans C++.
C++ n'autorise pas l'initialisation dynamique des données de thread en raison des améliorations futures dont peut bénéficier le mécanisme de stockage local des threads.
Si une DLL déclare des objets ou des données non locaux en tant que __declspec( thread ), elle risque de générer une erreur de protection si elle est chargée de façon dynamique. Une fois chargée à l'aide de LoadLibrary, la DLL risque de provoquer une panne du système chaque fois que le code fait référence à des données non locales __declspec( thread ). Dans la mesure où l'espace des variables globales d'un thread est alloué au moment de l'exécution, la taille de cet espace repose sur un calcul des besoins de l'application plus les besoins de toutes les DLL liées de façon statique. Quand vous utilisez LoadLibrary, il n'existe aucun moyen d'augmenter cet espace afin de tenir compte des variables locales du thread déclarées avec __declspec( thread ). Utilisez dans votre DLL des API TLS, telles que TlsAlloc, pour allouer un espace de stockage local des threads si la DLL peut d'être chargée par le biais de LoadLibrary.