Regole e limitazioni per TLS
Nella dichiarazione di oggetti e di variabili locali di thread associati in modo statico, è necessario attenersi alle istruzioni riportate di seguito.
L'attributo thread può essere applicato solo a dichiarazioni e definizioni di dati. Non può essere utilizzato per dichiarare o definire funzioni. Il codice seguente, ad esempio, genera un errore di compilazione:
#define Thread __declspec( thread ) Thread void func(); // This will generate an error.
Il modificatore thread può essere specificato solo per elementi di dati di tipo static. Sono inclusi gli oggetti di dati globali, sia static che extern, gli oggetti statici locali e i membri dati statici delle classi di C++. Non è possibile dichiarare oggetti di dati automatici con l'attributo thread. Il codice seguente genera errori di compilazione:
#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; }
È necessario che nelle dichiarazioni e nella definizione di un oggetto locale di thread sia specificato l'attributo thread. Il codice seguente genera ad esempio un errore:
#define Thread __declspec( thread ) extern int tls_i; // This will generate an error, since the int Thread tls_i; // declaration and definition differ.
L'attributo thread non può essere utilizzato come modificatore di tipo. Il codice seguente, ad esempio, genera un errore di compilazione:
char __declspec( thread ) *ch; // Error
Nelle classi di C++ non può essere utilizzato l'attributo thread. È possibile tuttavia creare un'istanza di oggetti di classe C++ con l'attributo thread. Il codice seguente, ad esempio, genera un errore di compilazione:
#define Thread __declspec( thread ) class Thread C // Error: classes cannot be declared Thread. { // Code }; C CObject;
Poiché la dichiarazione di oggetti C++ che utilizzano l'attributo thread è consentita, i due esempi seguenti sono equivalenti dal punto di vista semantico:
#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'indirizzo di un oggetto locale di thread non è considerato come costante, così come qualsiasi espressione che contenga tale indirizzo. Nel linguaggio C standard l'utilizzo dell'indirizzo di una variabile locale di thread come inizializzatore di un oggetto o puntatore, di conseguenza, non è consentito. Il codice seguente, ad esempio, viene contrassegnato come errore dal compilatore C:
#define Thread __declspec( thread ) Thread int tls_i; int *p = &tls_i; //This will generate an error in C.
Questa restrizione non si applica al linguaggio C++. Poiché C++ consente l'inizializzazione dinamica di tutti gli oggetti, è possibile inizializzare un oggetto tramite un'espressione che utilizza l'indirizzo di una variabile locale di thread. Questa operazione viene eseguita allo stesso modo della costruzione di oggetti locali di thread. Nel codice illustrato in precedenza non viene ad esempio generato un errore quando il codice viene compilato come file sorgente C++. Si noti che l'indirizzo di una variabile locale di thread è valido solo fino a quando il thread dal quale l'indirizzo è stato preso è esistente.
Il linguaggio C standard consente di inizializzare un oggetto o una variabile con un'espressione che include un riferimento a se stessa, ma solo per oggetti di tipo non statico. Sebbene il linguaggio C++ consenta in genere l'inizializzazione dinamica di oggetti con espressioni che includono un riferimento a se stesse, questo tipo di inizializzazione non è consentito con gli oggetti locali di thread. Di seguito è riportato un esempio:
#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++
Si noti che un'espressione sizeof in cui è incluso l'oggetto inizializzato non rappresenta un riferimento a se stessa ed è abilitata sia in C che in C++.
C++ non consente questo tipo di inizializzazione dinamica dei dati di thread, in relazione a possibili miglioramenti futuri della gestione della memoria locale di thread.
Nei sistemi operativi Windows antecedenti a Windows Vista, __declspec(thread) dispone di alcune limitazioni. Se in una DLL sono dichiarati dati o oggetti quali __declspec(thread), può verificarsi un errore di protezione se viene caricata in modo dinamico. Dopo il caricamento della DLL con LoadLibrary, ogni volta che il codice fa riferimento a dati di __declspec( thread ) viene generato un errore di sistema. Poiché lo spazio delle variabili globali per un thread viene assegnato in fase di esecuzione, la sua dimensione si basa sul calcolo dei requisiti dell'applicazione più i requisiti di tutte le DLL collegate in modo statico. Quando si utilizza LoadLibrary non è possibile estendere tale spazio per le variabili locali di thread dichiarate con __declspec( thread ). Utilizzare le API TLS, ad esempio TlsAlloc, nella DLL per l'assegnazione di TLS quando la DLL può essere caricata con LoadLibrary.