Ist die lokale statische Variableninitialisierung in C ++ 11 Thread-sicher?

Ich weiß, dass dies eine oft gestellte Frage ist, aber da es so viele Varianten gibt, würde ich es gerne wiederholen und hoffentlich eine Antwort haben, die den aktuellen Zustand widerspiegelt. Etwas wie

Logger& g_logger() {
    static Logger lg;
    return lg;
}

Wird der Konstruktor der Variablen lg garantiert nur einmal ausgeführt?

Aus früheren Antworten weiß ich, dass dies in C ++ 03 nicht der Fall ist. In C ++ 0x Entwurf wird dies erzwungen. Aber ich hätte gerne eine klarere Antwort darauf

  1. In C ++ 11 Standard (nicht Entwurf), ist das Thread-sichere Initialisierungsverhalten abgeschlossen?
  2. Wenn das oben genannte ja ist, sind sie in den neuesten Versionen populärer Compiler, nämlich gcc 4.7, vc 2011 und clang 3.0, richtig implementiert?
179
Ist es in VS2013 sicher, wenn wir sicherstellen, dass g_logger einmal von main() aufgerufen wird, bevor andere Threads ausgeführt werden?
hinzugefügt der Autor paulm, Quelle
Visual Studio 2012 Update 3 unterstützt dies nicht - ich habe es mit einem kurzen Testprogramm getestet und den Assembler-Code angeschaut.
hinzugefügt der Autor Tobias Langner, Quelle
Kann ich den Zweck einer solchen Funktion fragen? Welche Vorteile hat es gegenüber einem einfachen globalen Logger g_logger;
hinzugefügt der Autor Chris Lutz, Quelle
@ Balki, GCC hat es seit fast einem Jahrzehnt implementiert. Clang unterstützt es auch.
hinzugefügt der Autor Jonathan Wakely, Quelle
@Chris: Deterministische Initialisierung und Vermeidung des statischen Initialisierungsauftrags Fiasko. Lokale Statiken werden erst initialisiert, wenn die Funktion das erste Mal aufgerufen wird.
hinzugefügt der Autor Xeo, Quelle
Für die zweite Frage haben No Compiler das noch nicht implementiert.
hinzugefügt der Autor balki, Quelle
"Magische Statik" kommt endlich mit VS 2015: blogs.msdn.com/b/vcblog/archive/2014/11/17/…
hinzugefügt der Autor mlvljr, Quelle
Danke Xeo, das ist der Hauptgrund. Einige andere enthalten: 1. Normalerweise verwenden Client-Code in einem Protokollierungssystem es als Makro, wie LOG << "Ihr Protokoll" ..., und die Makros müssen einen deterministischen Zugriff auf den Protokollierer 2 haben. Der Protokollierer wird nicht erstellt wenn du es nicht benutzt. 3. Sie möchten wahrscheinlich nicht, dass Ihr Client mehrere Logger erstellt (es gibt Synchronisationsprobleme, usw.), so dass der Logger einen privaten Konstruktor hat, auf den nur der Freund g_logger() zugreifen kann.
hinzugefügt der Autor Ralph Zhang, Quelle
Es scheint auch nicht, Visual Studio 2013. Siehe die Zeile "Magic Statik" bei msdn.microsoft.com/de-de/library/vstudio/…
hinzugefügt der Autor rkjnsn, Quelle

2 Antworten

Der relevante Abschnitt 6.7:

Eine solche Variable wird initialisiert, wenn die Steuerung zum ersten Mal ihre Deklaration durchläuft; Eine solche Variable wird nach Abschluss ihrer Initialisierung als initialisiert betrachtet. [...] Wenn die Steuerung die Deklaration gleichzeitig während der Initialisierung der Variablen eingibt, muss die gleichzeitige Ausführung auf den Abschluss der Initialisierung warten.

Dann gibt es eine Fußnote:

Die Implementierung darf keinen Deadlock um die Ausführung des Initialisierers herum einführen.

Also ja, du bist in Sicherheit.

(Dies sagt natürlich nichts über den nachfolgenden Zugriff auf die Variable durch die Referenz.)

160
hinzugefügt
Es ist wichtig zu beachten, dass static Logger lg; nur Thread-sicher ist, wenn der Standardkonstruktor von Logger Thread-sicher ist, dh er greift nicht auf eine änderbare freigegebene Ressource zu intern, sagen wir über globale Variablen oder Singletons. Es sollte angemerkt werden, dass der Standard dies nur garantiert: wenn mehr ein Thread versucht, den Konstruktor gleichzeitig zu starten, wird nur einer von ihnen ausführen , der Rest wird warten für den Abschluss der Initialisierung. Der Standard gibt jedoch keine Garantie für die Thread-Sicherheit des Konstruktors selbst.
hinzugefügt der Autor Nawaz, Quelle
@KerrekSB: Ich habe auch erklärt, was ich damit meinte: d. H. Es greift intern nicht auf änderbare freigegebene Ressourcen zu . Nur weil nur ein Thread einen Konstruktor ausführt, heißt das nicht unbedingt, dass es Thread-sicher ist. Wenn es ungeschützte freigegebene Ressource ändert, wäre es nicht threadsicher.
hinzugefügt der Autor Nawaz, Quelle
@Nawaz: Warum muss der Konstrukteur Thread-sicher sein? Sie haben selbst gesagt, dass nur ein Thread den Konstruktor ausführen wird.
hinzugefügt der Autor Kerrek SB, Quelle
@ Nawaz: Nun, das stimmt, aber das ist auch eine vollständige Allgemeingültigkeit: Der gleichzeitige Zugriff auf gemeinsam genutzte Daten muss synchronisiert werden. Ich denke nicht, dass es irgendeinen Vorschlag gibt, dass die statische Initialisierung irgendwie eine Ausnahme von dieser Regel bieten würde, also dachte ich nicht, dass es sich lohnt, speziell zu rufen.
hinzugefügt der Autor Kerrek SB, Quelle
Es ist etwas schade, dass weder diese Frage noch diese Antwort den Ausdruck "Meyers Singleton" erwähnen
hinzugefügt der Autor Nemo, Quelle
Es sagt auch nichts über Leser der Variablen aus, was bedeutet, dass Sie TSAN-Probleme haben können, wenn Sie Leser haben, denen der statische Konstruktor nicht vorangestellt ist. Wenn Sie das obige Muster verwenden (foo & GetFooLog() {static foo bar; return bar;}, tritt das TSAN-Problem natürlich nicht auf.
hinzugefügt der Autor jesup, Quelle
hinzugefügt der Autor Ion Todirel, Quelle

--fenno-threadsafe-Statik auch erwähnenswert. In gcc:

Geben Sie keinen zusätzlichen Code aus, um die in C ++ ABI angegebenen Routinen für die Thread-sichere Initialisierung lokaler Statiken zu verwenden. Sie können diese Option verwenden, um die Codegröße in Code, der nicht threadsicher sein muss, geringfügig zu reduzieren.

Also, have a look at the old thread Are function static variables thread-safe in GCC?

9
hinzugefügt