Sind C ++ 11 thread_local-Variablen automatisch statisch?

83

Gibt es einen Unterschied zwischen diesen beiden Codesegmenten:

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

und

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

Hintergrundgeschichte: Ursprünglich hatte ich einen STATISCHEN Vektor V (zum Halten einiger Zwischenwerte wird er bei jeder Eingabe der Funktion gelöscht) und ein Single-Thread-Programm. Ich möchte das Programm in ein Multithreading-Programm verwandeln, also muss ich diesen statischen Modifikator irgendwie loswerden. Meine Idee ist es, jede Statik in thread_local umzuwandeln und sich um nichts anderes zu kümmern? Kann dieser Ansatz nach hinten losgehen?

Zuza
quelle
15
Eine thread_locallokale Variable zu haben macht zunächst keinen Sinn ... jeder Thread hat seinen eigenen Aufrufstapel.
Konrad Rudolph
1
Ursprünglich wurden mehrere C-Funktionen geschrieben, um die Adresse statischer oder globaler Variablen zurückzugeben. Es wurde später festgestellt, dass dies bei Verwendung in Multithread-Apps (z. B. errno, localtime) zu undurchsichtigen Fehlern führt. Darüber hinaus ist es manchmal sehr nachteilig, gemeinsam genutzte Variablen mit einem Mutex zu schützen, wenn eine Funktion von mehreren Threads aufgerufen wird, oder ein Thread-Kontextobjekt zwischen vielen Aufrufobjekten und -methoden übergeben zu müssen. Variablen, die für einen Thread lokal sind, lösen diese und andere Fragen.
Edwinc
3
@Konrad Rudolph, der lokale Variablen nur deklariert, staticanstatt static thread_localnicht eine Instanz der Variablen für jeden Thread zu initialisieren.
David
1
@davide Darum geht es weder bei mir noch beim OP. Wir sprechen nicht von staticvs static thread_local, sondern von autovs thread_local, wobei die Bedeutung von auto(dh automatischer Speicher) vor C ++ 11 verwendet wird.
Konrad Rudolph
1
Siehe auch So definieren Sie threadlokale lokale statische Variablen. . Ein kurzer Hinweis für einen Anwalt ... Microsoft- und TLS-Unterstützung wurden in Vista geändert. Siehe Thread Local Storage (TLS) . Die Änderung wirkt sich auf Dinge wie Singleton aus und kann zutreffen oder nicht. Wenn Sie das Abondware-Softwaremodell verwenden, sind Sie wahrscheinlich in Ordnung. Wenn Sie gerne mehrere Compiler und Plattformen unterstützen, müssen Sie möglicherweise darauf achten.
JWW

Antworten:

90

Nach dem C ++ Standard

Wenn thread_local auf eine Variable des Blockbereichs angewendet wird, wird der statische Speicherklassenspezifizierer impliziert, wenn er nicht explizit angezeigt wird

Das bedeutet also, dass diese Definition

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

ist äquivalent zu

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

Eine statische Variable ist jedoch nicht mit einer thread_local-Variablen identisch.

1 Alle mit dem Schlüsselwort thread_local deklarierten Variablen haben eine Thread-Speicherdauer. Die Speicherung für diese Entitäten dauert für die Dauer des Threads, in dem sie erstellt werden. Pro Thread gibt es ein bestimmtes Objekt oder eine bestimmte Referenz. Die Verwendung des deklarierten Namens bezieht sich auf die Entität, die dem aktuellen Thread zugeordnet ist

Zur Unterscheidung dieser Variablen führt der Standard einen neuen Begriff Thread-Speicherdauer zusammen mit statischer Speicherdauer ein.

Vlad aus Moskau
quelle
1
staticund externimplizieren daher keine Speicherklasse, sondern nur eine Verknüpfung für thread_local-Variablen, selbst in inneren Bereichen.
Deduplikator
4
@Deduplicator Block-Bereichsvariablen haben keine Verknüpfung. Ihr Lebenslauf ist also falsch. Wie ich in dem Beitrag geschrieben habe, haben sie Thread-Speicherdauer. Dies entspricht in der Tat der statischen Speicherdauer, wird jedoch auf jeden Thread angewendet.
Vlad aus Moskau
1
Wenn Sie das Externe hinzufügen, machen Sie eine Deklaration, keine Definition. So?
Deduplikator
1
@Deduplicator Dies bedeutet, dass Definitionen von Blockbereichsvariablen keine Verknüpfung haben.
Vlad aus Moskau
1
Ich habe es gerade in VS 2013 versucht und es wird angezeigt, dass TL-Variablen nicht dynamisch initialisiert werden können. Ich bin verwirrt.
v.oddou
18

Ja, "threadlokaler Speicher" ist "global" (oder "statischer Speicher") sehr ähnlich, nur dass anstelle von "Dauer des gesamten Programms" "Dauer des gesamten Threads" angezeigt wird. Daher wird eine blocklokale threadlokale Variable beim ersten Durchlauf der Steuerung durch ihre Deklaration initialisiert, jedoch separat in jedem Thread, und sie wird zerstört, wenn der Thread endet.

Kerrek SB
quelle
6

Bei Verwendung mit thread_local,static wird angedeutet , in Block-Rahmen (siehe @ Vlad Antwort), für eine Klasse Mitglied requied; Ich denke, bedeutet Verknüpfung für Namespace-Bereich.

Per 9.2 / 6:

Innerhalb einer Klassendefinition darf ein Mitglied nicht mit dem Speicherklassenspezifizierer thread_local deklariert werden, es sei denn, es wird auch als statisch deklariert

So beantworten Sie die ursprüngliche Frage:

Sind C ++ 11 thread_local-Variablen automatisch statisch?

Es gibt keine andere Wahl als Namespace-Scope-Variablen.

Gibt es einen Unterschied zwischen diesen beiden Codesegmenten:

Nein.

vehsakul
quelle
4

Der lokale Thread-Speicher ist statisch, verhält sich jedoch ganz anders als der einfache statische Speicher.

Wenn Sie eine Variable als statisch deklarieren, gibt es genau eine Instanz der Variablen. Das Compiler- / Laufzeitsystem garantiert, dass es irgendwann vor der tatsächlichen Verwendung für Sie initialisiert wird, ohne genau anzugeben, wann (einige Details wurden hier weggelassen).

C ++ 11 garantiert, dass diese Initialisierung threadsicher ist. Vor C ++ 11 wurde diese Thread-Sicherheit jedoch nicht garantiert. Beispielsweise

static X * pointer = new X;

Es können Instanzen von X verloren gehen, wenn mehr als ein Thread gleichzeitig den statischen Initialisierungscode trifft.

Wenn Sie einen Variablenthread lokal deklarieren, gibt es möglicherweise viele Instanzen der Variablen. Sie können sich diese als eine Karte vorstellen, die durch die Thread-ID indiziert wurde. Das bedeutet, dass jeder Thread eine eigene Kopie der Variablen sieht.

Wenn die Variable initialisiert wird, garantiert das Compiler- / Laufzeitsystem erneut, dass diese Initialisierung vor der Verwendung der Daten erfolgt und dass die Initialisierung für jeden Thread erfolgt, der die Variable verwendet. Der Compiler garantiert auch, dass die Initiierung threadsicher ist.

Die Thread-Sicherheitsgarantie bedeutet, dass es hinter den Kulissen eine Menge Code geben kann, damit sich die Variable so verhält, wie Sie es erwarten - insbesondere, wenn man bedenkt, dass der Compiler nicht im Voraus genau wissen kann, wie viele Threads dies tun werden sind in Ihrem Programm vorhanden und wie viele davon berühren die lokale Thread-Variable.

Dale Wilson
quelle
@Etherealone: ​​Interessant. Welche besonderen Informationen? Können Sie eine Referenz angeben?
Dale Wilson
1
stackoverflow.com/a/8102145/1576556 . Der Artikel in Wikipedia C ++ 11 erwähnt es, wenn ich mich recht erinnere.
Etherealone
1
Statische Objekte werden jedoch zuerst initialisiert und dann kopiert. Daher ist mir auch ein wenig unklar, ob die thread-sichere Initialisierung den vollständigen Ausdruck enthält. Dies ist jedoch wahrscheinlich der Fall, da es sonst nicht als threadsicher angesehen werden würde.
Etherealone