2012-04-05 22 views
2

Wenn ich eine Singleton-Klasse in C++ schreiben muss, verwende ich eine statische Variable, private Konstruktor & eine öffentliche statische Funktion, die ein Objekt der Klasse zurückgibt. In Multithread-Umgebungen hat der Code jedoch Probleme. Um zu vermeiden, dass mehrere Threads gleichzeitig auf die gleiche Variable zugreifen, ist der Boost-Threads-Mechanismus der beste für die Synchronisation? Ich meine, um eine Sperre/Mutex über die Ressource zu setzen/unscharfzusetzen. Ist in der C++ - Standardbibliothek etwas anderes eingebaut, wo ich Boost, Build-Sachen usw. nicht herunterladen muss? Ich habe von C++ Ox gehört, weiß aber nicht viel.Singleton Synchronisation C++

Antwort

7

C++ 98/03 haben nichts, Threads überhaupt zu unterstützen. Wenn Sie einen C++ 98- oder 03-Compiler verwenden, müssen Sie Boost oder etwas (mehr oder weniger) betriebssystemspezifisches wie z. B. Pthreads oder Win32-Threads verwenden.

C++ 11 eine einigermaßen vollständige Thread-Unterstützung Bibliothek, mit Mutexe, Schlösser, lokalen Thread-Speicher usw.

ich mich verpflichtet fühlen, jedoch darauf hin, dass es besser sein kann, zu sichern und Denken Sie darüber nach, ob Sie überhaupt einen Singleton benötigen/wollen. Um es schön zu sagen, das Singleton-Muster ist zu einem großen Grad in Ungnade gefallen.

Edit: Dies alles überspringend, habe ich eine Sache übersprungen, die ich sagen wollte: Zumindest wenn ich sie benutzt habe, wurden alle/alle Singletons vollständig initialisiert bevor irgendein zweiter Thread gestartet wurde. Das macht Bedenken hinsichtlich der Thread-Sicherheit bei ihrer Initialisierung völlig irrelevant. Ich nehme an, es könnte einen Singleton geben, den du nicht initialisieren kannst, bevor du sekundäre Threads startest, also musst du damit umgehen, aber zumindest fällt mir das als eine eher ungewöhnliche Ausnahme auf, mit der ich mich erst befassen würde, wenn/wenn unbedingt notwendig.

+0

Ich habe nichts gesehen, was darauf hindeutet, dass das Singleton-Muster bei den tatsächlichen Praktizierenden in Ungnade gefallen ist. (Natürlich wurde es auch nicht oft unter ihnen verwendet. Nur wenn es angemessen ist, was nicht oft ist.) –

+0

Ich habe viele 'Singleton's gesehen, aber keine davon notwendig. Mach einfach eins und gib es weiter. –

+0

Um es grob zu sagen: Ein 'Singleton' ist in der Regel ein Konstruktionsfehler. Obwohl ... stimme ich zu, dass es für Protokollierungsschemata bequem ist (viel mehr, als ein "Logger" -Objekt in jeder einzelnen Funktion zu übergeben). –

0

Für mich ist der beste Weg, einen Singleton mit C++ 11 zu implementieren ist:

class Singleton 
{ 
public: 
static Singleton & Instance() 
{ 
    // Since it's a static variable, if the class has already been created, 
    // It won't be created again. 
    // And it **is** thread-safe in C++11. 

    static Singleton myInstance; 

    // Return a reference to our instance. 
    return myInstance; 
} 

// delete copy and move constructors and assign operators 
Singleton(Singleton const&) = delete;    // Copy construct 
Singleton(Singleton&&) = delete;     // Move construct 
Singleton& operator=(Singleton const&) = delete; // Copy assign 
Singleton& operator=(Singleton &&) = delete;  // Move assign 

// Any other public methods 

protected: 
Singleton() 
{ 
    // Constructor code goes here. 
} 

~Singleton() 
{ 
    // Destructor code goes here. 
} 

// And any other protected methods. 
} 

Dies ist eine C++ 11-Funktion, aber mit dieser Art und Weise können Sie eine Thread-sicher Singleton erstellen. Laut neuem Standard muss man sich nicht mehr um dieses Problem kümmern. Die Objektinitialisierung wird nur von einem Thread durchgeführt, andere Threads warten bis zum Abschluss. Oder Sie können std :: call_once verwenden.

Wenn Sie einen exklusiven Zugriff auf die Ressourcen des Singletons vornehmen möchten, müssen Sie eine Sperre für diese Funktionen verwenden.

Die andere Art von Sperren:

Verwendung atomic_flg_lck:

class SLock 
{ 
public: 
    void lock() 
    { 
    while (lck.test_and_set(std::memory_order_acquire)); 
    } 

    void unlock() 
    { 
    lck.clear(std::memory_order_release); 
    } 

    SLock(){ 
    //lck = ATOMIC_FLAG_INIT; 
    lck.clear(); 
    } 
private: 
    std::atomic_flag lck;// = ATOMIC_FLAG_INIT; 
}; 

Verwendung atomic:

class SLock 
{ 
public: 
    void lock() 
    { 
    while (lck.exchange(true)); 
    } 

    void unlock() 
    { 
    lck = true; 
    } 

    SLock(){ 
    //lck = ATOMIC_FLAG_INIT; 
    lck = false; 
    } 
private: 
    std::atomic<bool> lck; 
}; 

Verwendung Mutex:

class SLock 
{ 
public: 
    void lock() 
    { 
    lck.lock(); 
    } 

    void unlock() 
    { 
    lck.unlock(); 
    } 

private: 
    std::mutex lck; 
}; 

Just for Windows-:

class SLock 
{ 
public: 
    void lock() 
    { 
    EnterCriticalSection(&g_crit_sec); 
    } 

    void unlock() 
    { 
    LeaveCriticalSection(&g_crit_sec); 
    } 

    SLock(){ 
    InitializeCriticalSectionAndSpinCount(&g_crit_sec, 0x80000400); 
    } 

private: 
    CRITICAL_SECTION g_crit_sec; 
}; 

Die Atom und und atomic_flg_lck den Faden halten in einem Spin Zahl. Mutex schläft nur den Thread. Wenn die Wartezeit zu lang ist, ist es vielleicht besser, den Thread zu schlafen.Der letzte "CRITICAL_SECTION" hält den Thread in einer Drehzählung, bis eine Zeit verbraucht ist, dann geht der Thread in den Ruhezustand.

Wie werden diese kritischen Abschnitte verwendet?

unique_ptr<SLock> raiilock(new SLock()); 

class Smartlock{ 
public: 
    Smartlock(){ raiilock->lock(); } 
    ~Smartlock(){ raiilock->unlock(); } 
}; 

Verwendung der Raii-Sprache. Der Konstruktor zum Sperren des kritischen Abschnitts und der Destruktor zum Entsperren.

Beispiel

class Singleton { 

    void syncronithedFunction(){ 
     Smartlock lock; 
     //..... 
    } 

} 

Diese Implementierung ist threadsicher und Ausnahme sicher, da die variable Sperre in dem Stapel gespeichert wird, so dass, wenn die Funktion Umfang (Ende-Funktion oder eine Ausnahme) beendet wird, wird der Destruktor bezeichnet werden.

Ich hoffe, dass Sie das hilfreich finden.

Danke !!