Singleton ohne Boost
-
Hi Folks,
Ich möchte eine "Application-Klasse erstellen, die ich auch von mehr al einem Thead nutzen kann. Also habe ich mit folgendes überlegt:
class CriticalSection { public: CriticalSection(); ~CriticalSection(); private: CriticalSection( const CriticalSection& ); CriticalSection& operator=( const CriticalSection& ); void Enter() const; void Leave() const; private: CRITICAL_SECTION m_cs; };
und
#include "CriticalSection.h" CriticalSection::CriticalSection(): m_cs() { ::InitializeCriticalSection( &m_cs ); } CriticalSection::~CriticalSection() { ::DeleteCriticalSection( &m_cs ); } void CriticalSection::Enter() const { ::EnterCriticalSection( &m_cs ); } void CriticalSection::Leave() const { ::LeaveCriticalSection( &m_cs ); }
und
#include "CriticalSection.h" class SectionLocker { public: SectionLocker( const CriticalSection &cs ); ~SectionLocker(); private: SectionLocker( const SectionLocker& ); SectionLocker& operator=( const SectionLocker& ); private: const CriticalSection &m_cs; };
und
#include "SectionLocker.h" SectionLocker::SectionLocker( const CriticalSection &cs ): m_cs( cs ) { m_cs.Enter(); } SectionLocker::~SectionLocker() { m_cs.Leave(); }
nun in der Application-Klasse
class Application { public: static Application &getInstance(); private: Application(); ~Application(); Application( const Application& ); Application& operator=( const Application& ); };
und
#include "Application.h" #include "SectionLocker.h" CriticalSection g_cs; Application::~Application() { //dtor } Application &Application::getInstance() { SectionLocker lock( g_cs ); static Application app; //Das wird garantiert zerstört. //Nur eine Instanz. return app; }
Ist das einen hinreichende Deklaration (Definition) der Klasse um sie von mehreren Threads aus zu nutzen?
Greetz
-
Der Code reicht damit
getInstance
von mehreren Threads aufgerufen werden kann. Er reicht aber natürlich nicht damit irgendwelche weiteren Funktionen die du der Application Klasse noch spendierst Thread-safe verwendet werden können.ps: mit einem C++11 konformen Compiler müsstest du die Initialisierung von "app" gar nicht selbst absichern.
-
hustbaer schrieb:
Der Code reicht damit
getInstance
von mehreren Threads aufgerufen werden kann. Er reicht aber natürlich nicht damit irgendwelche weiteren Funktionen die du der Application Klasse noch spendierst Thread-safe verwendet werden können.Ja, das stimmt. Muss ich mir dann für jede Klasse, die ich in Threads aufrufen will überlegen und sie auch entprechend behandeln.
hustbaer schrieb:
ps: mit einem C++11 konformen Compiler müsstest du die Initialisierung von "app" gar nicht selbst absichern.
stimmt. Da ist völlig richtig. Werden demnächst den Compiler umstellen auf die neuste Version. Ok. Danke.
-
Wie, absichern?
-
Wie gesagt, absichern. Wenn ein Thread auf Klasseninstanzen zugreift, dann spielt nur da die Musik. Was auch immer der Thread macht, Werte verändern, Methoden aufrufen... nichts zieht ihm den Stuhl unter'm Hintern weg. Sobald ein zweiter Thread ins Spiel kommt, dann darf nur ein Thread Methoden aufrufen bzw. Werte ändern. Um aber den Teil des Codes abzusichern der die Werte ändert (oder Methoden aufruft) und das dann auxh im selben Prozess wird die Criricalsection eingesetzt. Diese sperrt Programmteile vor mehrfachen Zugriff.
Oder ein einfaches Beispiel: Du (ein Thread) spielst Klavier. Ein zweiter Thread schließt den Deckel, wenn er geöffnet ist. Solange Du spielst befindest Du dich in der CriticalSection, der zweite Thread kann den Deckel nicht schießen. Sobald Du aufhörst (verlassen der Criticalsection) schließt der zweite Thread den Deckel. Und wieder sind ein paar Finger gerettet.
Nun ja, die App abzusichern ist eben nur ein Teil der Miete.
Zum Absichern gibt es auch ein paar Artikel:
http://www.drdobbs.com/cpp/c-and-the-perils-of-double-checked-locki/184405772
http://www.drdobbs.com/cpp/c-and-the-perils-of-double-checked-locki/184405726
http://stackoverflow.com/questions/1008019/c-singleton-design-pattern/1008289#1008289
http://stackoverflow.com/questions/1008019/c-singleton-design-pattern/1008289#1008289Mehrere Wege führen nach Rom.
Greetz
-
Achja, in deiner CriticalSection und SectionLocker Klasse sind natürlich noch Fehler drinnen. Ist mir beim 1. mal ansehen nicht aufgefallen.
CriticalSection
braucht keinen parameter im ctor - dasCRITICAL_SECTION
Member wird ja eh mitInitializeCriticalSection
initialisiert.- Die
SectionLocker
Klasse muss natürlich im ctor nenCriticalSection&
Parameter haben und nichtCRITICAL_SECTION&
. - Die
SectionLocker
Klasse darf dieCriticalSection
natürlich nicht by value speichern, sondern muss ne Referenz/nen Zeiger halten.
EDIT: - Beide Klassen müssen natürlich non-copyable sein.
Sind das Abschreibfehler? Weil eigentlich dürfte das was du hier gepostet hast nichtmal kompilieren.
-
FrankTheFox schrieb:
Hi Folks,
Ich möchte eine "Application-Klasse erstellen, die ich auch von mehr al einem Thead nutzen kann. Also habe ich mit folgendes überlegt:
class Application { public: Application(){} ~Application(); static Application &getInstance(); private: Application( const Application& ); Application& operator=( const Application& ); };
Hey,
dir ist aber schon bewusst was ein Singleton ist oder?
Weil mit deinen gezeigten Code kann man x Objekte von Application erstellen.2. Warum glaubst du das du ein singleton benoetigst? Viele Leute verwenden das Singleton Pattern falsch. imho bestes Beispiel: logging.
Aber liess dir ab besten ein paar Post im Forum durch bzw. such mal bei google nach singleton pattern
-
stuxn schrieb:
Hey,
...
dir ist aber schon bewusst was ein Singleton ist oder?
Weil mit deinen gezeigten Code kann man x Objekte von Application erstellen.Ja, dachte ich bis jetzt immer. Ein Singleton lässt nur eine Instanz zu.
stuxn schrieb:
2. Warum glaubst du das du ein singleton benoetigst? Viele Leute verwenden das Singleton Pattern falsch. imho bestes Beispiel: logging.
Aber liess dir ab besten ein paar Post im Forum durch bzw. such mal bei google nach singleton patternIch dachte, das ich nur eine Application-Klasse im kompletten Programm nutzen will. Funktioniert wahrscheinlich ( oder garantiert ) wenn die Application-Klasse global ist. Wobei ich mich frage was am Singleton so böse ist?
Greetz
-
-
Mal aus der Hüfte geschossen:
namespace blablupp { namespace { Application app; } Application* get_App() { return &app; } } blablupp::get_App()->funcX();
Ein globaler Namespace... (in der Übersetzungseinheit in der auch WinMain(...).. liegt)
Sollte es auch tun. Oder ist das völliger Schrott. Wäre dann nur noch das Absichern ganz ohne Singleton. Ein Singleton sollte aber auch Vorteile habe. Weil nur Nachteile (Antipattern...)
Greetz
-
FrankTheFox schrieb:
Mal aus der Hüfte geschossen:
namespace blablupp { namespace { Application app; } Application* get_App() { return &app; } } blablupp::get_App()->funcX();
Ein globaler Namespace... (in der Übersetzungseinheit in der auch WinMain(...).. liegt)
Sollte es auch tun. Oder ist das völliger Schrott. Wäre dann nur noch das Absichern ganz ohne Singleton. Ein Singleton sollte aber auch Vorteile habe. Weil nur Nachteile (Antipattern...)
Greetz
Was sollte das deiner Meinung nach bewirken? Ich versteh nicht was du machen willst.
Gruessle
-
Falscher button...
-
Es soll eine und nur eine Instanz von der Application-Klasse möglich sein. Weil es nur eine App geben kann. Nun ja, wenn Singleton so böse sind....
-
Singleton oder normal globale Variable oder "lokale" Variable die über ne Getter-Funktion global erreichbar gemacht wird -- macht fast keinen Unterschied.
Böse sind sie alle (*). Weil die Probleme die von allen gleichermassen verursacht werden viel schwerer wiegen als die Probleme die die eine Variante löst, die andere aber nicht.Eines der schlimmsten ist, dass es zu wuchernden Abhängigkeiten kommt. Wodurch Wiederverwendbarkeit jeder Art gezielt vernichtet wird. Und mit "jeder Art" meine ich auch so simple Dinge wie Copy & Paste Re-Use.
Hmmm, ich brauch da ne Einstellung inFunktionDieAnsichSchoenWiederverwendbarWaere()
. Ach, die hol ich mir gleich in der Funktion aus den globalen Applikations-Settings, macht ja keinen Sinn die überall rumzureichen.
Boom, you're dead.FrankTheFox schrieb:
Weil es nur eine App geben kann.
Erstmal bestreite ich das grundsätzlich. Und dann ... selbst wenn es nur eine App geben kann, das heisst noch lange nicht dass alle Klassen bzw. Funktionen die man zur Entwicklung dieser App entwickelt auch auf die App Zugriff haben sollten. Weil wenn sie es doch haben, naja, siehe Beispiel oben.
Und dabei geht es nicht nur um Code Re-Use. Es führt ganz allgemein zu schrecklich unübersichtlichen Programmen. Eine weitere schlimme Sache die man damit "schön" machen kann, also wo man in Versuchung gerät es zu machen, weil's ja "so praktisch ist", ist das "setzen und aufrufen" Anti-Pattern. (Das Ding hat offiziell nen anderen Namen, aber der ist mir leider entfallen. Gemeint ist Code ala
app.SetSomeOption(option); someClass.PerformSomeOperationThatUsesTheOptionWeJustSet();
- *schauder*)*:
Ausser wenn sie es nicht sind.
(Manchmal, in bestimmten Fällen, ganz selten, kann so ein Singleton die Wiederverwendbarkeit z.B. sogar verbessern. Aber hey, Ausnahmen gibt es immer. Grundsätzlich gilt aber: das was du gerade machst ist nicht eine dieser Ausnahmen. Es sei denn es ist doch eine, aber es ist eben keine. ;))
-
hustbaer schrieb:
...
FrankTheFox schrieb:
Weil es nur eine App geben kann.
Erstmal bestreite ich das grundsätzlich.
...Ok. Das mit dem Singleton ist tatsächlich keine gute Idee, weil die beschriebenen Probleme stärker wiegen als die Vorteile. Bislang hatte ich auch keine sinnvolle Verwendung für das Pattern. Was jetzt nicht heißen soll, nun muß das aber in den Kode weil sonst taugt der nix. Aber wenn ich eine Application-Klasse habe, wieso sollte es dann aber im Programm eine weitere geben? Die Klasse nutze ich ja um meinen Anwendungsspezifischen Krams reinzulegen. Darum dachte ich auch, jetzt wäre eine gute Gelegenheit gekommen das Muster zu nutzen.
Sonst mache ich die App (wenn es denn sein muss) global (->Namespace).
Gruß