Meyers Singleton auf dem Heap
-
TyRoXx schrieb:
Es gibt in C++ ein banales Sprachmittel, um auszudrücken, dass eine Ressource einmal vorkommt: Parameterübergabe.
[...]
Ta-da! Problem gelöst. Wenn man keine Singletons verwendet, stellt sich infund in denuser-Methoden gar nicht die Frage nach weiteren Instanzen vonresource.Ich stimme dir da voll zu das die Übergabe als Parameter wohl am Sinnvollsten ist, wie ich schonmal geschrieben hatte, und was auch Torsten Robitzki (siehe Unten) angesprochen hat gings mir dabei hauptsächlich initialisierungsprobleme zu lösen. Ich werde das Singleton für den Global Event Manager vermutlich auch als hauptsächlich als interne statische Variable (oder Klasse) der normalen Eventmanager Klasse verwenden, damit jeder Eventmanager bestimmte Informationen an den Globalen Eventmanager weiterleiten kann (z.b. registrieren sich diese dort). Der Global Event Manager erbt auch nicht von der Eventmanager KLasse, er steuert diese aber. Wie das am Ende genau aussieht wird sich noch zeigen.
TyRoXx schrieb:
Es geht bei Singletons nicht darum, 1-zu-N-Beziehungen zu modellieren oder irgendwie die Korrektheit zu fördern. Hört doch auf das zu behaupten. Es geht um erstens Faulheit und zweitens Unfähigkeit saubere Beziehungen zwischen Komponenten zu finden. Man muss sich überwinden das mal vernünftig zu lernen. Irgendwann macht es dann *klick* und man hat keine Angst mehr vor Klassen und Referenzen.
Ich bin sehr an Beispielen für sinnvolle Verwendung von Singletons interessiert. Habe noch nie eins gesehen.Naja, was die Initialisierung angeht finde ich schon das es die Korrektheit fördern kann, zumindest erleichters es erheblich zur rechten Zeit das voll initialisierte Objekt zur Hand zu haben, sicher gibts da andere Wege aber ich finde Singletons richtig eingesetzt immer noch nicht übel. Das es was mit Faulheit zu tun hat den einfacheren Ansatz zu wählen, da ist sicher was wahres dran. Ein einfaches Design kann aber auch Vorteile haben, unoetige Komplexität ist häufig eine Fehlerquelle. Ob es was mit Unfähigkeit zu tun hat wird sich in meinem Fall noch zeigen. Falls du einen Vorschlag hast wie ich meine 1:N Beziehungen, inclusive ordentlicher Initialisierung möglichst ausserhalb der Main hinbekommen werde ich mir den gerne anschauen.
TyRoXx schrieb:
Wenn ich deinen Code lese, sehe ich da ungefähr einen
io_service:
[...]
Der hat nur eine Queue und nicht zwei wie du. Dein Ansatz mitloopist nämlich seltsam. Warum machst du das so? Mit fehlt jetzt noch das ganze Bild. Wer ruftloopauf?boost::asio werd ich mir mal anschauen.
Die 2 Querys habe ich desswegen damit in die eine ungestört eingefügt, und aus der anderen rausgeholt werden kann. So beinflussen sich die beiden Funktionen möglichst wenig. Sonst müsste ich entweder die Liste jedes mal komplett sperren oder ich muss aufwendig checken das nicht beide Funktionen auf das gleiche Element zugreifen. Sicher gibts noch Threadsichere alternativen, aber da es speicher- und performancetechnisch relativ egal ist ob ich eine oder 2 Listen verwende habe ich mich für diese einfache Alternative entschieden.Loop wird im übrigen in der loop in der main funktion (meiner TestApp) aufgerufen, später soll das der Global Event Manager erledigen, dessen loop dann in der main zyklisch neu gestartet wird (ich meine mal gelesen zu haben das die Hauptschleife möglichst immer in der main sein sollte).
TyRoXx schrieb:
Verkettete Listen sind für Performance übrigens das schlechteste, was man nehmen kann.
Ja das stimmt leider, ich bin noch am überlegen was ich am besten mache. Sicherlich wäre eine Array-Queue (ich hab die auch schon als Listen oder Trees gesehen) in der richtigen Größe das beste. Ich hoffe insgeheim aber noch das ich der Zeit in der auf die Daten vom Heap geholt werden (beim Zugriff auf das listenelement) was anderes tun kann, Erlang ist bei sowas extrem gut das es Prozesse extrem schnell switchen kann.
Torsten Robitzki schrieb:
Ich habe überhaupt nichts gegen globale Variablen, solange sie ein guter Weg sind, das gestellte Problem zu lösen. std::cout, std::clog, std::cerr sind übrigens alles globale Variablen.
Mein Problem mit singletons ist, dass sie überhaupt kein Problem lösen. Das ursprüngliche Ziel war, sicher zu stellen, dass es exakt nur eine Instanz einer Klasse in einem Programm gibt. Aber warum sollte ich das wollen? Welche Anforderung kann ich damit erfüllen bzw. welches Design Ziel erreichen?
Kapselung, wie Du es schreibst, ist kein Design-Ziel. Lose Koppelung kann man durch Kapselung erreichen, da dadurch die Schnittstelle zu einer Komponente schmaler wird. Singletons führen aber zu allem anderen, aber nicht zu lose gekoppelten Komponenten. Du kannst ja mal versuchen, aus einem System mit vielen Singletons irgend etwas wieder zu verwenden. Meistens ziehst Du da an einem Ende und über die Singletons, hast Du dann das ganze System in der Hand.
In C++ setzt man singletons meist gar nicht für das ursprünglich Ziel ein, sondern um Probleme mit Initialisierungsreihenfolgen zu lösen. Dagegen spricht auch nichts, das sind dann Implementierungsdetails und so etwas möchte ich nicht als Schnittstelle bedienen müssen.
Genau dafuer ist es auch gedacht. Das mit den lose gekoppelten Komponenten ist ein guter Punkt. Mehr oder weniger meinte ich das mit "überall referenzieren", problematisch ist das aber auch mit Globalen variablen (cout aus nem Projekt zu schmeissen das auf ausgabe spezialisiert ist, ist bestimmt auch interessant). Das es nur eine Instanz geben soll liegt eben daran Begründet das der Global Event Manager alles andere verwalten soll, ob es letztenende notwendig ist das es nur eine Instanz geben kann wird sich zeigen, momentan ists die einfachste Lösung.
Kapselung ist da sicher nicht ganz der richtige, aber verwende Begriffe wie Kapselung, Schnittstelle und lose Kopplung oft synonym, was sicher nicht ganz richtig ist aber ich finde mehr oder weniger laeufts auf das Gleiche hinaus (Lose Kopplung ereiche ich über ne gute Schnittstellendefinition und das sich die Leute dran halten über Kapselung). Ich bin auch nicht ganz so fit was "normale" Softwareentwicklung angeht, ist auch einer der Gründe warum ich das meiste selbst machen will, aus Fehlern lernt man schliesslich.
Torsten Robitzki schrieb:
log::instance()->log_debug( "Foo" ); // vs. log::log_debug( "Foo" );da interessiert es mich doch nicht, dass die logging Komponente intern als Singleton implementiert ist, damit ich zu jedem Zeitpunkt darauf zugreifen kann.
Jup, ob ich überhaupt ne getInstance methode brauche werde ich sehen, momentan siehts eher nicht danach aus, aber ursprünglich hat der GEM von normalen Eventmanager geerbt, da war es natürlich einfacher die Methoden über instance aufzurufen als für alles eine statische Methode zu schreiben und weiterzuleiten. Mitlerweise ist er als statische Variable in die eventmanager Klasse hinein gewandert, da Größtenteils nur diese ihn verwenden, möglicherweise wandert er als private Klass auch ganz rein.
Torsten Robitzki schrieb:
P.S.: Anekdoten:
https://groups.google.com/d/topic/de.comp.objekt/i4Ana3y_tGc/discussion
https://www.xing.com/communities/posts/singletons-pro-strich-con-1004722838Ich werd mal reinschauen

Dank eurer Argumente für das Für und Wider von Singletons bin ich jetzt schonmal schlauer, bestärkt mich aber auch darin das Singletons in meinem Fall nicht unbedingt verkehrt sind, ob ich sie langfristig noch brauche wird sich zeigen.
Ich hoffe ich hab nicht zu viel Mist geschrieben, ist schon spät

Grüße
-
seelenquell schrieb:
Ich werde das Singleton für den Global Event Manager vermutlich auch als hauptsächlich als interne statische Variable (oder Klasse) der normalen Eventmanager Klasse verwenden, damit jeder Eventmanager bestimmte Informationen an den Globalen Eventmanager weiterleiten kann (z.b. registrieren sich diese dort).
Sowas haben wir sicherlich auch öfter mal in unserer Software... Nur stört es immer, wenn man dann plötzlich mehreren Instanzen von dem ganzen System haben will. Das kann man im Voraus alles schlecht abschätzen. Irgendwann wird dein System (nochmal) viel größer, und dann gibts lauter neue Entwickler und Anforderungen, und jemand sagt, ja, dieses Event Zeugs ist cool, will ich auch verwenden, aber mein Sub System hat nichts mit dem Rest zu tun und der globale Event Manager soll davon gar nichts mitbekommen.
-
seelenquell schrieb:
Technisch gesehen ist vermutlich nichts wirklich lockfrei.
Doch, lock-freie Datenstrukturen. Dinge wie try_lock bei einer Try-Mutex sind auch lock-free.
seelenquell schrieb:
Aber versteh bitte das ich keine "richtigen" Mutexe/Locks einsetzen will, das will ich unter allen Umstaenden vermeiden. Mir gehts dabei ums Prinzip, ich will darauf hinaus am Ende keine "sichtbaren" Locks zu haben. Ich hatte vor Jahren mal zimliche schwierigkeiten mit Locks, vermutlich habe ich desswegen so lange Clojure programmiert, wo es sprachlich garkeine Möglichkeit gibt Locks zu setzen, das war eine wirkliche Offenbarung. Daher kenne ich auch die Konzepte von Atomic, STM und async recht gut, in C++ muss ich mich da allerdings nochmal richtig durchwühlen (wie bei vielen anderen dingen auch).
Sorry, aber das klingt alles nicht so als ob du genügend Grundlagen/Vorkenntnisse hättest um sowas erfolgreich auf die Beine zu stellen.
-
Ist eine Weile her, aber ich hatte viel tun (nicht nur Programmieren).
Mechanics schrieb:
seelenquell schrieb:
Ich werde das Singleton für den Global Event Manager vermutlich auch als hauptsächlich als interne statische Variable (oder Klasse) der normalen Eventmanager Klasse verwenden, damit jeder Eventmanager bestimmte Informationen an den Globalen Eventmanager weiterleiten kann (z.b. registrieren sich diese dort).
Sowas haben wir sicherlich auch öfter mal in unserer Software... Nur stört es immer, wenn man dann plötzlich mehreren Instanzen von dem ganzen System haben will. Das kann man im Voraus alles schlecht abschätzen. Irgendwann wird dein System (nochmal) viel größer, und dann gibts lauter neue Entwickler und Anforderungen, und jemand sagt, ja, dieses Event Zeugs ist cool, will ich auch verwenden, aber mein Sub System hat nichts mit dem Rest zu tun und der globale Event Manager soll davon gar nichts mitbekommen.
Jup, könnte Aufwändig werden das im nachhinein auseinander zu wurschteln.
Das dachte ich mir letztenendes auch und hab einiges umgestellt. Den GlobalEventManager selbst ist nun kein Singleton mehr (und heisst nun EventServer), die Konstruktoren sind protected. Mein Singleton EventServer der intern verwendet wird erbt einfach von der KLasse.Das lässt sich per #define ECS3P_CUSTOM_EVENTSERVER CustomClass, auch einstellen. Der Rest ist eigentlich per Template Parameter konfigurierbar aber das ganze sah beim debuggen relativ unschön aus (bei typedef SystemManger<EventManagerBase, EventServer> EventManger; wobei der EventServer auch wieder ein Template ist wird leider immer der volle name incl aller paramter ausgegeben).
Deshalb erben EventManager und co die einzelnen Template Klassen einfach (aka class Eventmanager : public SystemManager<EventManager, EventServer_t>), EventServer_t wird dann einfach in einem #ifdef block als typedef gesetzt. Ob das so ideal ist kann ich nicht sagen, allerdings wars für die simpelste Lösung das von Benutzer konfigurierbar zu machen. Auf dem gleiche weg lassen sich auch Systeme komplett deaktivieren.
hustbaer schrieb:
seelenquell schrieb:
Technisch gesehen ist vermutlich nichts wirklich lockfrei.
Doch, lock-freie Datenstrukturen. Dinge wie try_lock bei einer Try-Mutex sind auch lock-free.
Muss ich mir mal anschauen, eigentlich waere mir STM ja am liebsten, aber bis 2017 werde ich da vermutlich noch mindestens warten müssen wenn ich keine externe lib verwenden will. Je nachdem wie lange es dauert bis die Compiler es unterstützen eher noch länger.
hustbaer schrieb:
seelenquell schrieb:
Aber versteh bitte das ich keine "richtigen" Mutexe/Locks einsetzen will, das will ich unter allen Umstaenden vermeiden. Mir gehts dabei ums Prinzip, ich will darauf hinaus am Ende keine "sichtbaren" Locks zu haben. Ich hatte vor Jahren mal zimliche schwierigkeiten mit Locks, vermutlich habe ich desswegen so lange Clojure programmiert, wo es sprachlich garkeine Möglichkeit gibt Locks zu setzen, das war eine wirkliche Offenbarung. Daher kenne ich auch die Konzepte von Atomic, STM und async recht gut, in C++ muss ich mich da allerdings nochmal richtig durchwühlen (wie bei vielen anderen dingen auch).
Sorry, aber das klingt alles nicht so als ob du genügend Grundlagen/Vorkenntnisse hättest um sowas erfolgreich auf die Beine zu stellen.
Das Projekt ist ja auch sowas wie mein "mal schauen was ich hinkrieg". Wenns nicht nach meinen Vorstellungen machbar ist muss ich eben auf ein anderes Konzept zurückgreifen. Ich hab aber nunmal mit Locks in Java (ich kenne eigentlich bisher Mutex und Lock nur synonym) relativ schlechte Erfahrungen gemacht. Nachdem ich mit Java angefangen hatte hab ich mich ne Weile mit nem Websever Projekt von nem Bekannten beschäftigt, das ist quasi ständig in nen Deadlock gerannt, irgendwann hat dann der Bekannte auch aufgehört und nochmal von vorne angefangen (da hatte ich schon lange keinen Nerf mehr). Clojure, das ja auch auf der JVM läuft und mit Java voll kompatibel ist, hat die Probleme nicht. Daten sind per se Immutable, es gibt standartmäßig keine Variablen, ändere ich eine Datenstruktur wird eine Kopie erzeugt. Um Variable Werte speichern zu können muss ich da auf Atomics, Agents oder Refs (transactional references) zurückgreifen, welche alle Thread safe sind und nicht blockieren. Clojure wurde auch mit dem Ziel entwickelt weshalbs auch relativ flott ist. Im vergleich zu anderen HTTP Servern ist der code von Clojure-Ring wohl desshalb auch zimlich einfach gehalten.
Sorry fuer den langen Text, aber ich wollte nur sagen das ich mit Concurrency in anderen Sprachen schon einiges zu tun hatte. Ob und wie sich das auf C++ übertragen lässt wird sich zeigen. Da die Benchmarks zum Thema Nebenläufigkeit in C++ die ich kenne (vor allem auf http://www.grimm-jaud.de) zimlich ernüchternd ausfallen, habe ich beschlossen innerhalb meiner Systeme weitgehend darauf zu verzichten, es hat immer nur ein Thread gleichzeitig Zugriff auf einen Verbund von Systemen (das steuert der Eventmanager), eine Ausnahme ist atm nur die Eventqueue in die von aussen gepusht werden kann, und das Positioning System, dessen variabler Teil vom Eventmanager beschrieben wird.
Vielleicht kennt ja zufällig jemand nen guten link zum Thema multithreading Performance, habe leider bisher wenig gefunden, http://www.grimm-jaud.de ist da leider auch nicht besonders aussagekräftig.