Archiviazione thread-local (TLS)

L'archiviazione thread-local è il metodo attraverso il quale ogni thread incluso in un processo multithreading specifico può allocare posizioni in cui archiviare dati specifici del thread. I dati specifici del thread associati dinamicamente (runtime) sono supportati tramite l'API TLS (TlsAlloc). Win32 e il compilatore Microsoft C++ supportano ora dati associati staticamente (tempo di caricamento) per thread oltre all'implementazione dell'API esistente.

Implementazione del compilatore per TLS

C++11: l'identificatore thread_local di classe di archiviazione è il modo consigliato per specificare l'archiviazione locale del thread per gli oggetti e i membri della classe. Per altre informazioni, vedere Classi di archiviazione (C++).

MSVC fornisce anche un attributo specifico di Microsoft, thread, come modificatore della classe di archiviazione estesa. Usare la __declspec parola chiave per dichiarare una thread variabile. Nel codice seguente, ad esempio, viene dichiarata una variabile locale di thread di tipo integer e quindi inizializzata con un valore:

__declspec( thread ) int tls_i = 1;

Regole e limitazioni

Le linee guida seguenti devono essere osservate quando si dichiarano variabili e oggetti thread-local associati staticamente. Queste linee guida si applicano sia al thread che alle thread_local:

  • L'attributo thread può essere applicato solo alle dichiarazioni di classe e ai dati e alle definizioni. Non può essere usato nelle dichiarazioni o nelle definizioni di funzione. Il codice seguente, ad esempio, genera un errore di compilazione:

    __declspec( thread )void func();     // This will generate an error.
    
  • Il thread modificatore può essere specificato solo negli elementi di dati con static extent. Inclusi gli oggetti dati globali (e static extern), gli oggetti statici locali e i membri dati statici delle classi C++. Gli oggetti dati automatici non possono essere dichiarati con l'attributo thread . Il codice seguente genera errori del compilatore:

    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;
    }
    
  • Le dichiarazioni e la definizione di un oggetto locale del thread devono tutti specificare 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 __declspec( thread )tls_i;        // declaration and definition differ.
    
  • L'attributo thread non può essere usato come modificatore di tipo. Il codice seguente, ad esempio, genera un errore di compilazione:

    char __declspec( thread ) *ch;        // Error
    
  • Poiché la dichiarazione di oggetti C++ che usano l'attributo thread è consentita, i due esempi seguenti sono semanticamente equivalenti:

    __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'indirizzo di un oggetto locale del thread non è considerato costante e qualsiasi espressione che coinvolge tale indirizzo non è considerata un'espressione costante. Nello standard C, l'effetto consiste nel impedire l'uso dell'indirizzo di una variabile locale del thread come inizializzatore per un oggetto o un puntatore. Ad esempio, il codice seguente viene contrassegnato come errore dal compilatore C:

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

    Questa restrizione non si applica in C++. Poiché C++ permette l'inizializzazione dinamica di tutti gli oggetti, è possibile inizializzare un oggetto usando un'espressione che usa l'indirizzo di una variabile thread-local. È fatto proprio come la costruzione di oggetti locali thread. Ad esempio, il codice illustrato in precedenza non genera un errore quando viene compilato come file di origine C++. L'indirizzo di una variabile locale del thread è valido solo se il thread in cui è stato acquisito l'indirizzo esiste ancora.

  • Lo standard C consente l'inizializzazione di un oggetto o di una variabile con un'espressione che implica un riferimento a se stesso, ma solo per gli oggetti di extent non statico. Sebbene C++ consenta in genere l'inizializzazione dinamica di oggetti con un'espressione che implica un riferimento a se stesso, questo tipo di inizializzazione non è consentito con gli oggetti locali del thread. Ad esempio:

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

    Un'espressione sizeof che include l'oggetto da inizializzare non rappresenta un riferimento a se stesso ed è abilitata sia in C che in C++.

    C++ non consente l'inizializzazione dinamica dei dati dei thread a causa di possibili miglioramenti futuri alla funzionalità di archiviazione locale del thread.

  • Nei sistemi operativi Windows precedenti a Windows Vista presenta __declspec( thread ) alcune limitazioni. Se una DLL dichiara dati o oggetti come __declspec( thread ), può causare un errore di protezione se caricato dinamicamente. Dopo che la DLL viene caricata con LoadLibrary, causa un errore di sistema ogni volta che il codice fa riferimento ai __declspec( thread ) dati. Poiché lo spazio delle variabili globali per un thread viene allocato in fase di esecuzione, le dimensioni di questo spazio sono basate sul calcolo dei requisiti dell'applicazione sommati ai requisiti di tutte le DLL collegate staticamente. Quando si usa LoadLibrary, non è possibile estendere questo spazio per consentire le variabili locali del thread dichiarate con __declspec( thread ). Usare le API TLS, ad esempio TlsAlloc, nella DLL per allocare TLS se la DLL potrebbe essere caricata con LoadLibrary.

Vedi anche

Multithreading con C e Win32