Frage

Ich habe über Thread-sichere Singleton Muster gelesen hier:

http://en.wikipedia.org/wiki /Singleton_pattern#C.2B.2B_.28using_pthreads.29

Und es sagt am Ende, dass der einzig sichere Weg ist pthread_once zu verwenden -., Die unter Windows nicht verfügbar ist

Ist das der nur Weg Thread-sicher Initialisierung gewährleisten?

Ich habe diesen Thread gelesen auf SO:

Fädeln sicher faul Bau eines Singleton in C ++

Und scheint auf atomarer Ebene OS Swap andeuten und Vergleichsfunktion, die ich auf Windows nehmen ist:

http://msdn.microsoft.com/en-us/library /ms683568.aspx

Kann man das tun, was ich will?

Edit:. Ich würde faul Initialisierung mag und dort, immer nur eine Instanz der Klasse

Jemand auf einer anderen Website erwähnt einen globalen innerhalb eines Namespace (und er einen Singleton als anti-Muster beschrieben) - wie kann es ein „anti-Muster“ sein

?

akzeptierte Antwort:
Ich habe akzeptiert Joshs Antwort , wie ich bin mit Visual Studio 2008 - NB: Für zukünftige Leser, wenn Sie diesen Compiler nicht verwenden (oder 2005) - Setzen Sie die akzeptierte Antwort nicht verwenden !!

Edit: Der Code funktioniert mit Ausnahme der return-Anweisung in Ordnung - ich erhalte eine Fehlermeldung: Fehler C2440: 'Return': kann nicht von 'flüchtiger Singleton *' auf 'Singleton *' konvertieren. Soll ich den Rückgabewert ändern volatil sein Singleton *?

Edit: Offenbar const_cast <> wird den flüchtigen Qualifier entfernen. Nochmals vielen Dank an Josh.

War es hilfreich?

Lösung

Wenn Sie verwenden Visual C ++ 2005/2008 Sie das Doppel verwenden können Karomuster Sperren, da „ flüchtige Variablen verhalten, als Zäune ". Dies ist der effizienteste Weg, um einen faulen initialisierten Singleton zu implementieren.

MSDN Magazine:

Singleton* GetSingleton()
{
    volatile static Singleton* pSingleton = 0;

    if (pSingleton == NULL)
    {
        EnterCriticalSection(&cs);

        if (pSingleton == NULL)
        {
            try
            {
                pSingleton = new Singleton();
            }
            catch (...)
            {
                // Something went wrong.
            }
        }

        LeaveCriticalSection(&cs);
    }

    return const_cast<Singleton*>(pSingleton);
}

Wenn Sie Zugriff auf das Singleton benötigen, rufen Sie einfach GetSingleton (). Das erste Mal aufgerufen wird, wird der statische Zeiger initialisiert werden. Nachdem sie initialisiert ist, wird verhindert, dass der NULL-Check für die Verriegelung nur den Zeiger zu lesen.

NICHT dies nur jeden Compiler auf, da es nicht tragbar ist. Der Standard übernimmt keine Garantie, wie dies funktionieren wird. Visual C ++ 2005 fügt explizit auf die Semantik von flüchtigem dies möglich zu machen.

Sie müssen erklären und den kritischen Abschnitt initialisieren an anderer Stelle im Code. Aber dass die Initialisierung ist billig, so faul Initialisierung ist in der Regel nicht von Bedeutung.

Andere Tipps

Eine einfache Art und Weise zu gewährleisten Cross-Plattform-Thread-sicher Initialisierung eines Singleton ist es explizit auszuführen (über einen Aufruf zu einer statischen Member-Funktion auf dem Singleton) im Hauptthread der Anwendung < strong> vor Ihre Anwendung alle anderen Threads beginnt (oder zumindest alle anderen Threads, die die Singleton zugreifen).

Die Gewährleistung Thread sichere Zugang zum Singleton dann auf die übliche Art und Weise mit mutexes / kritischen Abschnitten erreicht wird.

Verzögerte Initialisierung kann auch über einen ähnlichen Mechanismus erreicht werden. Das übliche Problem mit dieser auftrat, ist, dass der Mutex zu schaffen Thread-Sicherheit erforderlich ist oft in der Singleton initialisiert selbst, die gerade das Faden-Sicherheitsproblem drückt den Mutex / kritischen Abschnitts die Initialisierung. Eine Möglichkeit, dieses Problem zu überwinden, ist zu erstellen und einen Mutex / kritischen Abschnitt im Hauptthread der Anwendung initialisiert werden dann übergeben Sie es an die Singleton über einen Aufruf zu einer statischen Member-Funktion. Das Schwergewicht der Initialisierung des Singletons kann dann in einem Thread-sichere Weise erfolgen diese vorinitialisiert mutex / kritischen Abschnitt verwendet wird. Zum Beispiel:

// A critical section guard - create on the stack to provide 
// automatic locking/unlocking even in the face of uncaught exceptions
class Guard {
    private:
        LPCRITICAL_SECTION CriticalSection;

    public:
        Guard(LPCRITICAL_SECTION CS) : CriticalSection(CS) {
            EnterCriticalSection(CriticalSection);
        }

        ~Guard() {
            LeaveCriticalSection(CriticalSection);
        }
};

// A thread-safe singleton
class Singleton {
    private:
        static Singleton* Instance;
        static CRITICAL_SECTION InitLock;
        CRITICIAL_SECTION InstanceLock;

        Singleton() {
            // Time consuming initialization here ...

            InitializeCriticalSection(&InstanceLock);
        }

        ~Singleton() {
            DeleteCriticalSection(&InstanceLock);
        }

    public:
        // Not thread-safe - to be called from the main application thread
        static void Create() {
            InitializeCriticalSection(&InitLock);
            Instance = NULL;
        }

        // Not thread-safe - to be called from the main application thread
        static void Destroy() {
            delete Instance;
            DeleteCriticalSection(&InitLock);
        }

        // Thread-safe lazy initializer
        static Singleton* GetInstance() {
            Guard(&InitLock);

            if (Instance == NULL) {
                Instance = new Singleton;
            }

            return Instance;
        }

        // Thread-safe operation
        void doThreadSafeOperation() {
            Guard(&InstanceLock);

            // Perform thread-safe operation
        }
};

Allerdings gibt es gute Gründe, die Verwendung von Singletons ganz zu vermeiden (und warum sie manchmal bezeichnet als anti-Muster ):

  • Sie sind im Wesentlichen verherrlicht globale Variablen
  • Sie können zwischen verschiedenen Teilen einer Anwendung
  • zu hohen Kupplung führen
  • Sie können (in Swapping echte Singletons mit gefälschten Implementierungen aufgrund der schwer)
  • Unit-Tests komplizierter oder unmöglich machen

Eine Alternative ist die Verwendung eines ‚logischen Singletons‘ zu machen, wodurch Sie eine einzelne Instanz einer Klasse in dem Haupt-Thread erstellen und initialisieren und an die Objekte zu übergeben, die sie benötigen. Dieser Ansatz kann unhandlich werden, wo es viele Objekte, die Sie als Singletons erstellen möchten. In diesem Fall können die unterschiedlichen Objekte in ein einziges ‚Kontext‘ Objekt gebündelt werden, die dann um bei Bedarf übergeben wird.

Während ich die akzeptierte Lösung wie, ich habe gerade ein weitere vielversprechende Führung und dachte, ich soll sie hier teilen: One-Time-Initialisierung (Windows)

Sie können ein OS primitiv wie Mutex oder kritischen Abschnitt verwenden, um die Threadsicherheit Initialisierung sorgen jedoch dafür wird ein Overhead jedes Mal, wenn Ihr Singleton Zeiger zugegriffen wird (durch den Erwerb einer Sperre) entstehen. Es ist auch nicht tragbar.

Es gibt eine klärende Punkt, den Sie für diese Frage berücksichtigen müssen. Haben Sie benötigen ...

  1. Das eine und nur eine Instanz einer Klasse jemals tatsächlich erstellt
  2. Viele Instanzen einer Klasse erstellt werden können, aber es sollte nur eine wahre endgültige Instanz der Klasse

Es gibt viele Proben auf dem Netz diese Muster in C ++ zu implementieren. Hier ist ein Code Project Beispiel

Im Folgenden wird erläutert, wie es in C # zu tun, aber genau das gleiche Konzept gilt für jede Programmiersprache, die die Singletonmuster unterstützen

http://www.yoda.arachsys.com/csharp/singleton.html

Was Sie brauchen, um zu entscheiden, ist wheter Sie faul Initialisierung wollen oder nicht. Verzögerte Initialisierung bedeutet, dass das Objekt innerhalb des Singletons enthaltenen auf dem ersten Anruf an sie erstellt wird Ex:

MySingleton::getInstance()->doWork();

, wenn dieser Anruf ist nicht erst später gemacht, besteht die Gefahr einer Racebedingung zwischen den Fäden wie in dem Artikel erläutert. Wenn Sie jedoch setzen

MySingleton::getInstance()->initSingleton();

am Anfang des Codes, wo Sie davon ausgehen, wäre es sicher Thread wird, dann sind Sie nicht mehr faul Initialisierung, werden Sie „etwas“ mehr Rechenleistung, wenn die Anwendung startet benötigen. Allerdings wird es eine Menge Kopfschmerzen über Rennbedingungen lösen, wenn Sie dies tun.

Wenn Sie für eine tragbare und einfachere Lösung suchen, Sie könnte sich steigern.

boost :: call_once verwendet werden können, für die Threadsicherheit Initialisierung.

Es ist ziemlich einfach zu bedienen und wird Teil des nächsten C ++ 0x-Standard sein.

Die Frage der Singleton nicht erforderlich ist faul ausgebaute oder nicht. Da viele Antworten davon ausgehen, dass, Ich gehe davon aus, dass für den ersten Satz diskutieren:

Angesichts der Tatsache, dass die Sprache selbst Thread-Bewusstsein ist nicht, und sowie die Optimierungstechnik ist sehr schwer, ein tragbaren zuverlässig c ++ singleton Schreiben (wenn nicht unmöglich), siehe „ C ++ und die Perils of doppelt geprüft Sperren " von Scott Meyers und Andrei Alexandrescu.

Ich habe viele der Antwort Ausweg gesehen durch die Verwendung Critical Objekt auf Windows-Plattform zu synchronisieren, aber Critical ist nur Thread-sicher, wenn alle Threads auf einem einzigen Prozessor ausgeführt wird, heute ist es wahrscheinlich nicht wahr.

MSDN zitieren: „Die Threads eines einzelnen Prozesses einen kritischen Abschnitt Objekt für gegenseitigen Ausschluss Synchronisation verwenden können.“

.

Und http: / /msdn.microsoft.com/en-us/library/windows/desktop/ms682530(v=vs.85).aspx

clearify es weiter:

ein kritischer Abschnitt Objekt stellt Synchronisation ähnlich zu dem durch ein Mutex-Objekt zur Verfügung gestellt, mit der Ausnahme, dass ein kritischer Abschnitt kann durch die Gewindegänge eines einzelnen Prozesses nur verwendet werden.

Nun, wenn „faul konstruiert“ ist nicht erforderlich, ist die folgende Lösung, die sowohl Quer Modul sicher und Thread-sicher und sogar tragbar:

struct X { };

X * get_X_Instance()
{
    static X x;
    return &x;
}
extern int X_singleton_helper = (get_X_instance(), 1);

Es ist Cross-Modul-sicher, da wir lokal scoped statisches Objekt statt Datei verwenden / Namensraum scoped globales Objekt.

Es ist Thread-sicher, weil:. X_singleton_helper auf den richtigen Wert zugewiesen werden muss, bevor die Eingabe Haupt- oder DllMain Es ist nicht faul konstruierte auch aufgrund dieser Tatsache), in diesem Ausdruck das Komma ist ein Operator, nicht Interpunktion

Explizit „extern“ verwenden hier, um die Compiler optimieren zu verhindern out (Bedenken über Scott Meyers Artikel, der große Feind ist Optimierer.), Und auch statisch-Analyse-Tool wie PC-lint schweigen machen. "Vor main / DllMain" ist Scott meyer "Single-Threaded-Start Teil" in dem Namen "Effective C ++ 3." Artikel 4.

Aber ich bin nicht ganz sicher, ob Compiler die get_X_instance den Anruf zu optimieren erlaubt () gemäß dem Sprachstandard, bitte kommentieren.

Es gibt viele Möglichkeiten, die Threadsicherheit Singleton * Initialisierung auf Windows zu tun. In der Tat sind einige von ihnen sogar plattformübergreifend. In der SO Thread, dass Sie verbunden, sie wurden für eine Singleton suchen, der gemächlich in C aufgebaut ist, die ein bisschen mehr spezifisch ist, und kann ein bisschen schwieriger zu tun, richtig sein, die Feinheiten des Speichermodells gegeben Sie arbeiten unter .

  • , die Sie sollten nie verwenden
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top