
TL36
Ist die folgende Implementierung mit verzögerter Initialisierung von Singleton
(Meyers’ Singleton) Thread sicher?
static Singleton& instance()
{
static Singleton s;
return s;
}
Wenn nicht, warum und wie kann man es threadsicher machen?

Groo
Im C++11, es ist threadsicher. Entsprechend der Standard, §6.7 [stmt.dcl] p4
:
Wenn die Steuerung gleichzeitig in die Deklaration eintritt, während die Variable initialisiert wird, wird die gleichzeitige Ausführung wartet zum Abschluss der Initialisierung.
GCC- und VS-Unterstützung für das Feature (Dynamische Initialisierung und Zerstörung mit Parallelitätauch bekannt als Magic Statics auf MSDN) ist wie folgt:
Danke an @Mankarse und @olen_gam für ihre Kommentare.
Im C++03, war dieser Code nicht threadsicher. Es gibt einen Artikel von Meyers namens “C++ und die Gefahren des doppelt geprüften Sperrens” die Thread-sichere Implementierungen des Musters diskutiert, und die Schlussfolgerung ist mehr oder weniger, dass (in C++03) das vollständige Sperren um die Instanziierungsmethode im Grunde der einfachste Weg ist, um eine ordnungsgemäße Parallelität auf allen Plattformen sicherzustellen, während die meisten Formen doppelt sind -geprüfte Sperrmustervarianten können auf bestimmten Architekturen unter Racebedingungen leiden, es sei denn, Anweisungen sind mit strategisch platzierten Speicherbarrieren verschachtelt.

Michael Burr
Um Ihre Frage zu beantworten, warum es nicht threadsicher ist, liegt es nicht daran, dass der erste Aufruf erfolgt instance()
muss den Konstruktor für aufrufen Singleton s
. Um threadsicher zu sein, müsste dies in einem kritischen Abschnitt erfolgen, und im Standard ist es jedoch nicht erforderlich, dass ein kritischer Abschnitt verwendet wird (der Standard ist bis heute völlig still zu Threads). Compiler implementieren dies oft durch eine einfache Überprüfung und Inkrementierung eines statischen booleschen Werts – aber nicht in einem kritischen Abschnitt. Etwas wie der folgende Pseudocode:
static Singleton& instance()
{
static bool initialized = false;
static char s[sizeof( Singleton)];
if (!initialized) {
initialized = true;
new( &s) Singleton(); // call placement new on s to construct it
}
return (*(reinterpret_cast<Singleton*>( &s)));
}
Hier ist also ein einfaches Thread-sicheres Singleton (für Windows). Es verwendet einen einfachen Klassen-Wrapper für das Windows-Objekt CRITICAL_SECTION, sodass wir den Compiler automatisch initialisieren können CRITICAL_SECTION
Vor main()
wird genannt. Idealerweise würde eine echte RAII-Klasse für kritische Abschnitte verwendet, die Ausnahmen behandeln kann, die auftreten können, wenn der kritische Abschnitt gehalten wird, aber das würde den Rahmen dieser Antwort sprengen.
Die grundlegende Operation ist die, wenn eine Instanz von Singleton
angefordert, eine Sperre genommen, das Singleton erstellt, falls erforderlich, dann die Sperre freigegeben und die Singleton-Referenz zurückgegeben.
#include <windows.h>
class CritSection : public CRITICAL_SECTION
{
public:
CritSection() {
InitializeCriticalSection( this);
}
~CritSection() {
DeleteCriticalSection( this);
}
private:
// disable copy and assignment of CritSection
CritSection( CritSection const&);
CritSection& operator=( CritSection const&);
};
class Singleton
{
public:
static Singleton& instance();
private:
// don't allow public construct/destruct
Singleton();
~Singleton();
// disable copy & assignment
Singleton( Singleton const&);
Singleton& operator=( Singleton const&);
static CritSection instance_lock;
};
CritSection Singleton::instance_lock; // definition for Singleton's lock
// it's initialized before main() is called
Singleton::Singleton()
{
}
Singleton& Singleton::instance()
{
// check to see if we need to create the Singleton
EnterCriticalSection( &instance_lock);
static Singleton s;
LeaveCriticalSection( &instance_lock);
return s;
}
Mann – das ist eine Menge Mist, um “global besser zu machen”.
Die Hauptnachteile dieser Implementierung (wenn ich nicht einige Fehler durchschlüpfen ließ) sind:
- wenn
new Singleton()
wirft, wird die Sperre nicht freigegeben. Dies kann behoben werden, indem anstelle des einfachen Objekts, das ich hier habe, ein echtes RAII-Sperrobjekt verwendet wird. Dies kann auch dazu beitragen, Dinge portabel zu machen, wenn Sie etwas wie Boost verwenden, um einen plattformunabhängigen Wrapper für die Sperre bereitzustellen.
- dies garantiert Thread-Sicherheit, wenn die Singleton-Instanz danach angefordert wird
main()
aufgerufen wird – wenn Sie es vorher aufrufen (wie bei der Initialisierung eines statischen Objekts), funktionieren die Dinge möglicherweise nicht, weil die CRITICAL_SECTION
möglicherweise nicht initialisiert.
- Jedes Mal, wenn eine Instanz angefordert wird, muss eine Sperre ausgeführt werden. Wie gesagt, dies ist eine einfache Thread-sichere Implementierung. Wenn Sie eine bessere benötigen (oder wissen möchten, warum Dinge wie die Double-Check-Lock-Technik fehlerhaft sind), lesen Sie die in Groos Antwort verlinkten Dokumente.
Ein Blick auf den nächsten Standard (Abschnitt 6.7.4) erklärt, wie die statische lokale Initialisierung Thread-sicher ist. Sobald dieser Abschnitt des Standards weit verbreitet ist, wird Meyers Singleton die bevorzugte Implementierung sein.
Ich bin schon mit vielen Antworten nicht einverstanden. Die meisten Compiler implementieren die statische Initialisierung bereits auf diese Weise. Die einzige bemerkenswerte Ausnahme ist Microsoft Visual Studio.
Die richtige Antwort hängt von Ihrem Compiler ab. Es kann sich dafür entscheiden machen es threadsicher; es ist nicht “natürlich” threadsicher.
Ist die folgende Implementierung […] Thread sicher?
Auf den meisten Plattformen ist dies nicht Thread-sicher. (Fügen Sie den üblichen Haftungsausschluss hinzu, der erklärt, dass der C++-Standard nichts über Threads weiß, also sagt er rechtlich nicht, ob er es ist oder nicht.)
Wenn nicht, warum […]?
Der Grund dafür ist, dass nichts die gleichzeitige Ausführung von mehr als einem Thread verhindert s
‘ Konstrukteur.
wie macht man es threadsicher?
“C++ und die Gefahren des doppelt geprüften Sperrens” von Scott Meyers und Andrei Alexandrescu ist eine ziemlich gute Abhandlung zum Thema Thread-sichere Singletons.

Sellibitze
Wie MSalters sagte: Es hängt von der C++-Implementierung ab, die Sie verwenden. Überprüfen Sie die Dokumentation. Zur anderen Frage: “Wenn nein, warum?” — Der C++-Standard erwähnt noch nichts über Threads. Aber die kommende C++-Version kennt Threads und gibt explizit an, dass die Initialisierung von statischen Locals Thread-sicher ist. Wenn zwei Threads eine solche Funktion aufrufen, führt ein Thread eine Initialisierung durch, während der andere blockiert und auf deren Beendigung wartet.
9957800cookie-checkIst die Meyers-Implementierung des Singleton-Musterthreads sicher?yes
Kann mir bitte jemand erklären, warum dies nicht Thread-sicher ist. Die Artikel, die in den Links erwähnt werden, diskutieren die Thread-Sicherheit mit einer alternativen Implementierung (mit einer Zeigervariablen, dh einem statischen Singleton *pInstance).
– TL36
2. November 2009 um 14:50 Uhr
Siehe: stackoverflow.com/questions/449436/…
– Martin York
2. November 2009 um 19:37 Uhr
Siehe: stackoverflow.com/questions/1008019/c-singleton-design-pattern/…
– Martin York
2. November 2009 um 19:43 Uhr
Mögliches Duplikat der Singleton-Instanz, die als statische Variable der GetInstance-Methode deklariert ist
– Trevor Boyd Smith
4. September 2018 um 11:45 Uhr