Double-Checked-Lock Singleton in C++11
Ist die folgende singleton-Implementierung data-race free?
static std::atomic<Tp *> m_instance;
...
static Tp &
instance()
{
if (!m_instance.load(std::memory_order_relaxed))
{
std::lock_guard<std::mutex> lock(m_mutex);
if (!m_instance.load(std::memory_order_acquire))
{
Tp * i = new Tp;
m_instance.store(i, std::memory_order_release);
}
}
return * m_instance.load(std::memory_order_relaxed);
}
Ist die std::memory_model_acquire
von der Last operation überflüssig? Ist es möglich, weiter zu entspannen, laden und store-Operationen durch Umschalten Sie std::memory_order_relaxed
? In diesem Fall ist das erwerben/release-Semantik std::mutex
genug, um Garantie für die Richtigkeit oder eine weitere std::atomic_thread_fence(std::memory_order_release)
ist auch erforderlich, um zu gewährleisten, dass die Schreibvorgänge auf den Speicher der Konstruktor geschehen, bevor die entspannte speichern? Doch ist die Nutzung der Zaun entspricht das Geschäft mit memory_order_release
?
BEARBEITEN: Dank der Antwort von John, ich kam mit der folgenden Implementierung, sollte die data-race-free. Auch wenn die innere Kraft nicht-atomaren überhaupt, habe ich beschlossen, zu verlassen, entspannter laden, dass es keinen Einfluss auf die Leistung. Im Vergleich zu immer einer äußeren Belastung mit dem Erwerb Speicher, um die thread_local Maschinen verbessert die Leistung der Zugriff auf die Instanz der etwa eine Größenordnung.
static Tp &
instance()
{
static thread_local Tp *instance;
if (!instance &&
!(instance = m_instance.load(std::memory_order_acquire)))
{
std::lock_guard<std::mutex> lock(m_mutex);
if (!(instance = m_instance.load(std::memory_order_relaxed)))
{
instance = new Tp;
m_instance.store(instance, std::memory_order_release);
}
}
return *instance;
}
- Ich sehe einen Fehler, dein code ist nicht das singleton-kostenlos.
- Wenn Sie nicht wissen, wie zu implementieren eines Singleton sicher, dann sollten Sie wahrscheinlich nicht verwenden Sie eine.
- Von Ihnen positiv bewertet werden. C++11 <atomic> ist ein neuer header mit der neuen Terminologie. Fragen, wie es zu benutzen, um sicher zu implementieren double-checked locking ist eine ausgezeichnete Frage, die ich mir vorstellen, werden viele Menschen sich Fragen.
- Das double-checked-locking-Muster hat gezeigt, dass unsicher von Scott Meyers und Andrei Alexandrescu. Siehe aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf für details. Matthäus
- Wo würde
m_mutex
definiert werden? - Gilman: das Papier bezieht sich speziell auf
[15] ISO/IEC 14882:
1998 - Wenn es die thread-lokalen, wie es ein singleton? Und wenn es die thread-lokalen und seine Adresse nicht herum, warum tun Sie müssen atomaren laden überhaupt? Aber ohne die thread_local es scheint gut zu sein.
- Meiner Meinung nach, der gesamte Begriff der double-checked-locking als solche ist Quatsch. Der einzig sichere (und nicht Total wirr, unintellegible -) Weg zu erreichen, was double-checked locking versucht zu tun ist erstellen einer singleton aus dem Haupt-thread, bevor irgendwelche anderen threads erstellt werden. Trivial zu tun, keine komplizierten voodoo-Bedarf deutlich weniger Aufwand, und garantiert, um zu arbeiten.
- Bonelli: Sie erkennen, dass der obige code mit
thread_local
ist nicht implementiert singleton-pattern? Ihre Instanzen werden mehrere, einer pro thread. - Nein werden Sie nicht, da alle threads teilen sich die gleiche Instanz über
m_instance
. Diethread_local instance
ist nur ein cache für die Instanz, um zu vermeiden Erzeugung einer atomaren Belastung pro Zugang - Du hast Recht, ich habe übersehen, dass. Obwohl gegeben, wie sind Atomare liest implementiert auf den meisten Plattformen und wie ist
thread_local
umgesetzt GCC, derzeit, ich denke, es wäre besser zu tun, ohne diethread_local
variable und gegen Atomare Lesen statt.
Du musst angemeldet sein, um einen Kommentar abzugeben.
Dass die Umsetzung nicht Rennen-kostenlos. Die atomic-Shop von singleton, während es verwendet release-Semantik, wird nur die Synchronisierung mit der passenden erwerben Bedienung—das ist der Last-Betrieb, der bereits bewacht von den mutex.
Ist es möglich, dass die äußeren entspannter laden, Lesen einen nicht-null-Zeiger vor dem sperren thread beendet die Initialisierung des singleton.
Den Erwerb, die ist bewacht von dem Schloss, auf der anderen Seite, ist überflüssig. Es wird eine Synchronisierung mit einem Geschäft mit release-Semantik in einem anderen thread, aber zu diesem Zeitpunkt (Dank der mutex -) der einzige thread, vielleicht kann die store ist der aktuelle thread. Diese Last muss nicht einmal sein atomic—keine Geschäfte passieren kann, von einem anderen thread aus.
Sehen Anthony Williams " - Serie auf C++0x-multithreading.
Ich denke das ist ein gute Frage, und John Calsbeek hat die richtige Antwort.
Jedoch nur klar zu sein, eine faule singleton ist am besten umgesetzt, wobei die klassischen Meyers singleton. Es hat garantiert die richtige Semantik in C++11.
§ 6.7.4
Den Meyers-singleton ist bevorzugt, dass der compiler kann aggressiv Optimierung der gleichzeitigen code. Der compiler würde noch mehr eingeschränkt, wenn es um die Erhaltung der Semantik einer
std::mutex
. Außerdem, den Meyers-singleton ist 2 Zeilen und praktisch unmöglich zu bekommen, falsch.Hier ist ein klassisches Beispiel für ein Meyers-singleton. Einfach, elegant, und gebrochen in c++03. Aber einfach, elegant und leistungsfähig in c++11.
static
lokalen Variablen. Geist hinzufügen eines standard-Zitat?Siehe auch call_once.
Wo würden Sie zuvor die Verwendung eines singleton, etwas zu tun, aber nicht wirklich verwenden Sie das zurückgegebene Objekt für alles, call_once kann die bessere Lösung.
Für eine regelmäßige singleton-Sie könnte tun call_once um eine (Globale?) variable und dann return, die variable...
Vereinfacht für die Kürze:
Genau einer Ausführung genau eine der Funktionen, übergeben als f zu den Anrufungen in der Gruppe (gleiche fahne-Objekt), durchgeführt wird.
Kein Aufruf in der Gruppe zurück, bevor die oben genannten Ausführung der ausgewählten Funktion erfolgreich abgeschlossen ist