Le variabili thread_local di C ++ 11 sono automaticamente statiche?


85

C'è una differenza tra questi due segmenti di codice:

void f() {
    thread_local vector<int> V;
    V.clear();
    ... // use V as a temporary variable
}

e

void f() {
    static thread_local vector<int> V;
    V.clear();
    ... // use V as a temporary variable
}

Backstory: originariamente avevo un vettore STATICO V (per contenere alcuni valori intermedi, viene cancellato ogni volta che entro nella funzione) e un programma a thread singolo. Voglio trasformare il programma in uno multithreading, quindi in qualche modo devo sbarazzarmi di questo modificatore statico. La mia idea è trasformare ogni statica in thread_local e non preoccuparti di nient'altro? Può questo approccio ritorcersi contro?


17
Avere una thread_localvariabile locale non ha senso per cominciare ... ogni thread ha il proprio stack di chiamate.
Konrad Rudolph

1
Diverse funzioni C sono state originariamente scritte per restituire l'indirizzo di variabili statiche o globali. Successivamente si è scoperto che questo portava a bug oscuri quando utilizzato in app multi-thread (ad esempio errno, localtime). Inoltre, a volte è molto dannoso proteggere le variabili condivise con un mutex quando una funzione viene chiamata da più thread o dover passare un oggetto contesto di thread tra molti oggetti e metodi di chiamata .. Le variabili locali a un thread risolvono questi e altri problemi.
edwinc

3
@Konrad Rudolph dichiara le variabili locali esclusivamente come staticinvece di static thread_localnon inizializzare un'istanza della variabile per ogni thread.
davide

1
@davide Non è questo il punto, né per me né per l'OP. Non stiamo parlando di staticvs static thread_localma piuttosto di autovs thread_local, utilizzando il significato pre-C ++ 11 di auto(cioè archiviazione automatica).
Konrad Rudolph

1
Vedi anche Come definire variabili statiche locali locali di thread? . Una breve nota per un avvocato in lingua ... Il supporto di Microsoft e TLS è cambiato in Vista; vedere Thread Local Storage (TLS) . La modifica influisce su cose come quella di Singleton e può essere applicata o meno. Se stai usando il modello software abondware, probabilmente starai bene. Se sei felice di supportare più compilatori e piattaforme, potresti dover prestare attenzione.
jww

Risposte:


94

Secondo lo standard C ++

Quando thread_local viene applicato a una variabile di ambito di blocco, lo specificatore di classe di archiviazione statico è implicito se non appare esplicitamente

Quindi significa che questa definizione

void f() {
    thread_local vector<int> V;
    V.clear();
    ... // use V as a temporary variable
}

è equivalente a

void f() {
    static thread_local vector<int> V;
    V.clear();
    ... // use V as a temporary variable
}

Tuttavia, una variabile statica non è la stessa di una variabile thread_local.

1 Tutte le variabili dichiarate con la parola chiave thread_local hanno una durata di archiviazione del thread. L'archiviazione per queste entità durerà per la durata del thread in cui vengono create. Esiste un oggetto o un riferimento distinto per thread e l'uso del nome dichiarato si riferisce all'entità associata al thread corrente

Per distinguere queste variabili, lo standard introduce un nuovo termine durata della memorizzazione dei thread insieme alla durata della memorizzazione statica.


1
statice externquindi non implica la classe di archiviazione ma solo il collegamento per le variabili thread_local anche negli ambiti interni.
Deduplicator

4
@Deduplicator Le variabili di ambito del blocco non hanno alcun collegamento. Quindi il tuo curriculum è sbagliato. Come ho scritto nel post hanno una durata di archiviazione dei thread. È infatti uguale alla durata dell'archiviazione statica ma applicata a ogni thread.
Vlad da Mosca l'

1
Se aggiungi l'extern, stai facendo una dichiarazione, non una definizione. Così?
Deduplicator

1
@Deduplicator Quindi significa che le definizioni delle variabili di ambito del blocco non hanno alcun collegamento.
Vlad da Mosca l'

1
L'ho appena provato in VS 2013 e si dice "Le variabili TL non possono essere inizializzate dinamicamente". Sono perplesso.
v.oddou

19

Sì, "archiviazione locale del thread" è molto simile a "globale" (o "archiviazione statica"), solo che invece di "durata dell'intero programma" hai "durata dell'intero thread". Quindi una variabile locale del thread locale del blocco viene inizializzata la prima volta che il controllo passa attraverso la sua dichiarazione, ma separatamente all'interno di ogni thread, e viene distrutta quando il thread termina.


6

Quando viene utilizzato con thread_local, staticè implicito nell'ambito del blocco (vedere la risposta di @ Vlad), richiesto per un membro della classe; Immagino che significhi collegamento per l'ambito dello spazio dei nomi.

Per 9.2 / 6:

All'interno di una definizione di classe, un membro non deve essere dichiarato con thread_local storage-class-specifier a meno che non sia dichiarato anche static

Per rispondere alla domanda originale:

Le variabili thread_local di C ++ 11 sono automaticamente statiche?

Non c'è scelta, ad eccezione delle variabili di ambito dello spazio dei nomi.

C'è una differenza tra questi due segmenti di codice:

No.


4

L'archiviazione locale del thread è statica ma si comporta in modo abbastanza diverso dalla semplice archiviazione statica.

Quando dichiari una variabile statica, c'è esattamente un'istanza della variabile. Il sistema compilatore / runtime garantisce che verrà inizializzato per te qualche tempo prima di usarlo effettivamente, senza specificare esattamente quando (alcuni dettagli sono omessi qui).

C ++ 11 garantisce che questa inizializzazione sarà thread-safe, tuttavia prima di C ++ 11 questa thread safety non era garantita. Per esempio

static X * pointer = new X;

potrebbe perdere istanze di X se più di un thread ha colpito il codice di inizializzazione statica allo stesso tempo.

Quando si dichiara una variabile thread locale, ci sono potenzialmente molte istanze della variabile. Potresti pensare a loro come se fossero in una mappa indicizzata da thread-id. Ciò significa che ogni thread vede la propria copia della variabile.

Ancora una volta, se la variabile viene inizializzata, il sistema compilatore / runtime garantisce che questa inizializzazione avverrà prima che i dati vengano utilizzati e che avvenga l'inizializzazione per ogni thread che utilizza la variabile. Il compilatore garantisce inoltre che l'avvio sarà thread-safe.

Le garanzie di thread safety significano che ci può essere un bel po 'di codice dietro le quinte per far sì che la variabile si comporti come ti aspetti, specialmente considerando che il compilatore non ha modo di sapere in anticipo esattamente quanti thread saranno esistono nel tuo programma e quanti di questi toccheranno la variabile locale del thread.


@Etherealone: ​​interessante. Quali informazioni particolari? Potete fornire un riferimento?
Dale Wilson

1
stackoverflow.com/a/8102145/1576556 . L'articolo di Wikipedia C ++ 11 lo menziona se ricordo bene.
Etherealone

1
Tuttavia, gli oggetti statici vengono prima inizializzati, quindi viene assegnata la copia. Quindi sono anche un po 'poco chiaro se l'inizializzazione thread-safe include l'espressione completa. Probabilmente lo fa perché altrimenti non sarebbe considerato thread-safe.
Etherealone
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.