threadsafe?
-
Das mit dem "generalisierte Policy-Bibliothek nur für sich selbst"-schreiben birgt immer das Risiko, dass man das ganze nicht gut durchtestet, weil man eben ja eigentlich nur seinen eigenen Code als Benutzer hat, wobei dennoch der negative Punkt der schlechteren Lesbarkeit bleibt. Ich finde, dort ist ein guter Ansatz, erst solche Template-Policy-Basisklassen einzubauen, wenn man sie wirklich braucht. Sowas ist ja eigentlich schnell nachgerüstet.
-
und mir ist gerade aufgefallen, dass clock() für einen timer beim threading mit die unglücklichste wahl ist. Volkards link zur docu sagts eindeutig: clock() muss bei mehreren threads nicht wall-time zurückgeben. Und auf meinem System tut es das auch nicht.
Also, ist die Klasse Thradsafe: nee, die geht sofort kaputt, wenn sie mit mehreren Threads verwendet wird.
Als nächster Punkt, und das ist durchaus nur meine Bescheidene Meinung: in vielen Fällen muss nicht eine Klasse THreadsafe gemacht werden, sodnern ihre Verwendung. Wer einfach nur überall extensiv lockt, und das dann auch noch, wie du, in einem Spiel macht, fliegt auf die Schnauze.
Kann man nicht statt eines scoped-locks so eine Art guard-Klasse verwenden und damit das locking aus den Klassen enternen?
Ich dachte an etwas, das in etwa wie folgt in der Verwendung aussieht:
Locker<Timer> timer;//erzeugt einen timer sowie einen mutex //einmalige verwendung, macht lock(mutex); timer.get_time(); unlock(mutex); timer->get_time(); {//mehrfache verwendung, spart lock-operationen Lock<Timer> lock = timer->lock();//lock(mutex) lock->get_time();//0 overhead lock->get_time();//o overhead }//hier unlock(mutex)dann muss man sich nicht in allen Klassen mit Threading beschäftigen, sondern nur die Klassen, von denen man weiß, dass sie mit Objekten aus unterschiedlichen Threads hantieren müssen.
-
otze schrieb:
Ich dachte an etwas, das in etwa wie folgt in der Verwendung aussieht:
{//mehrfache verwendung, spart lock-operationen Lock<Timer> lock = timer->lock();//lock(mutex) lock->get_time();//0 overhead lock->get_time();//o overhead }//hier unlock(mutex)dann muss man sich nicht in allen Klassen mit Threading beschäftigen, sondern nur die Klassen, von denen man weiß, dass sie mit Objekten aus unterschiedlichen Threads hantieren müssen.
Das ist generell eine sehr gute Idee.

Kack auf die Threading-Model-Policies! Nicht das Objekt weiß, wann gelockt werden muss, sondern der Anwender.
(Dazu kommt in Kürze nochwas extremes...)
-
Hey, den Ansatz finde ich cool. Muss ich mir für meine Problemstellung noch etwas einfallen lassen, weil ich bei mir eigentlich an vielen Stellen nur gewisse Interfaces zu IN einem Objekt habe (Also das Objekt bietet die Interfaces über einen Reflektions-Mechanismus an, das bedeutet noch lange nicht, dass es von diesen ableitet). Und so wird natürlich die Kopplung zwischen den involvierten Hilfsklassen und den Objekt wieder etwas vergrößert. Hrmm, hrmmm. Außerdem, wenn man den Lock dann in den Schnittstellen definiert, hat man virtuelle Aufrufe, selbst bei Objekten, die gar nicht gelockt werden brauchen. Hrmm, Hrmm.
Edit: Also bei mir muss grundsätzlich jedes Objekt eigentlich gelockt werden, weil alles parallel abläuft, von daher ist der Ansatz natürlich immer noch viel stringenter, aber der Vorteil mit der Performance erübrigt sich eigentlich. Bleibt natürlich noch der Vorteil, dass man so einfach über mehrere Operationen locken kann, dafür hatte ich bei einer speziellen Schnittstelle bei mir eben ein solches Lock vorgesehen, wie volkard es beschrieben hat. Und dann so lange nicht mehr damit gearbeitet, dass ich es bis eben wieder vergessen habe. Mal sehen, wieviel Mehrarbeit die erhöhte Kopplung kostet, wenn ich das überall durchziehe...
-
Der Ansatz ist wirklich genail, was ich mir da an zeit, übersichtlichkeit performance,... spare

ich mach mich glaube mal direkt ran
-
Der Nachteil ist, dass man sich recht einfach potentielle Deadlocks einfangen kann:
Locker<A> objA; Locker<B> objB; void threadFunc1(){ Lock<A> locka = objA.lock(); Lock<B> lockb = objB.lock(); } void threadFunc2(){ Lock<B> lockb = objB.lock();//oops, ich vertausche die beiden Zeilen... Lock<A> locka = objA.lock(); }be carefull

-
gamer8o4 schrieb:
Der Ansatz ist wirklich genail, was ich mir da an zeit, übersichtlichkeit performance,... spare

ich mach mich glaube mal direkt ran
Beispiel, wo es wirklich rocken würde, wenn man logfile und << selber implementieren würde:
logfile<<"Hello"<<' '<<"world"<<endl;Das lockt mindestens bei den vier Teilen viermal, bestimmt fünfmalm weil endl=='\n'<<flush, und wenn man Pech hat sogar pro Zeichen.
UND es vermischt die Ausgaben der Threads! cout muss doch gar nicht Threadsicher sein in dem Sinne, daß nix doppelt oder zu wenig ausgegeben wird. Verschwendete Rechenzeit. Denn das hülft gar genau gar nix, wenn ich tausend Web-Client-Threads dranhängen habe und die was loggen. Und teuer ist es auch noch.
Lock(logfile)<<"Hello"<<' '<<"world"<<endl;und es gibt keine Vermischung. Und es ist viel billiger.
Und wenn ich single-Threaded unterwegs bin brauche ich das locken im Herzen von cout auch nicht.
-
otze schrieb:
Ich dachte an etwas, das in etwa wie folgt in der Verwendung aussieht:
Locker<Timer> timer;//erzeugt einen timer sowie einen mutex //einmalige verwendung, macht lock(mutex); timer.get_time(); unlock(mutex); timer->get_time(); {//mehrfache verwendung, spart lock-operationen Lock<Timer> lock = timer->lock();//lock(mutex) lock->get_time();//0 overhead lock->get_time();//o overhead }//hier unlock(mutex)Man könnte auch sowas ähnliches machen, dass man einen einfachen Timer hat, der nur Funktionen anbietet, die kein lock brauchen und eine Methode die einen LockedTimer zurückgibt, der dann alle Methoden hat.
-
ghfjgfdjg schrieb:
Man könnte auch sowas ähnliches machen, dass man einen einfachen Timer hat, der nur Funktionen anbietet, die kein lock brauchen und eine Methode die einen LockedTimer zurückgibt, der dann alle Methoden hat.
Sowas ist mir sogar mal gelungen, ohne daß ich verschiedene Klassen anbieten musste.
Ist aber ein paar Tage her, hab die Details nicht mehr im Kopf.Ein const-Objekt o. Jeder kann sich davon beliebig eine const-Referenz ziehen im Funktionsaufruf oder eine Zeiger. Das war für alle, die nicht locken müssen. Innendrin mußte das Objekt halt mutable benutzen.
Und mit Lock(o) kann man sich eine non-const-Referenz besorgen, die zu einer const-Ref im Aufruf konvertiert wird, wärend die non-const-ref noch lebt oder irgend sowas. Es war sehr verfrickelt, hat zwar geklappt, aber ich gab es schnell wieder auf wegen weil es total unintuitiv war.Wie steht's eigentlich mit volatile?
uint32_t globalClock::get_time(); uint32_t globalClock::set_time() volatile;Ich hab noch nie volatile bei Methoden gesehen.
-
den Ansatz finde ich cool ... Ansatz ist wirklich genail
Ohja, sehr spektakulaer ... Lockguards sind jetzt nicht gerade neu und Varianten gibt es viele.
UND es vermischt die Ausgaben der Threads! cout muss doch gar nicht Threadsicher sein in dem Sinne, daß nix doppelt oder zu wenig ausgegeben wird. Verschwendete Rechenzeit. Denn das hülft gar genau gar nix, wenn ich tausend Web-Client-Threads dranhängen habe und die was loggen. Und teuer ist es auch noch.
Im Vergleich mit Schreiben auf
coutwahrscheinlich trotzdem billig. Hier ein Beispiel mit monitor<T>: http://channel9.msdn.com/Shows/Going+Deep/C-and-Beyond-2012-Herb-Sutter-Concurrency-and-Parallelism , geht in die Richtung von ghfjgfdjg's Vorschlag.Der Nachteil ist, dass man sich recht einfach potentielle Deadlocks einfangen kann:
Immer wenn 2 Locks involviert sind, besteht diese Gefahr. Aber das ist auch nur Grundlagenkram.
Ich hab noch nie volatile bei Methoden gesehen.
http://www.drdobbs.com/cpp/volatile-the-multithreaded-programmers-b/184403766
Ansonsten kann man sich auch mal http://www.youtube.com/playlist?list=PL1835A90FC78FF8BE ansehen, bevor man multithreaded programmiert.
-
knivil schrieb:
...
Mit dem falschen Fuß aufgestanden?
-
Unnoetige Templatemonster schreiben fuer simple Timerklassen und noch nicht mal Grundlagen verstehen. Ausserdem ist fast alles konstruktive Kritik. Das hat auch nichts mit "nicht optimal" zu tun wie in "Meine liebe Hassliste".
-
knivil schrieb:
Im Vergleich mit Schreiben auf
coutwahrscheinlich trotzdem billig.Jo, die Windows-Konsole stinkt performance-mäßig total ab. Unglaublich. In Linux tue ich 300k Zeilen mit "find /" in Sekündchen raus. Win schneckt unglaublich ab da. Aber ich vermute, daß das auch strategisch ist, MS will nicht neue Konsole-Anwendungen haben, deswegen wird dahingehend nix mehr investiert.
Zum Spaß hatte ich mal ein eigenes cout gebastelt, das wenn cout auf die Konsole geht, die Konsoleabmessungen abliest, einen Ringbuffer für Ausgabezeilen dieser Größe anlegt und nur alle 10ms den kompletten Inhalt raustut. Das war recht schnell. Zeilenweise gelockt. Locking mußte bei dem Ansatz schon sein, weil der Reinschreibe-Thread nicht der Auf-Den-Bildschirm-Tu-Thread ist. Und es war eher Folge des Gedankens des zeilenweisen lockens, was ich für Logging bei multithreading haben mag.
Hab leider den Sourcecode verschludert und nur noch eine exe lebt, die gibt die Primzahlen bis 10Mio aus.
http://volkard.de/download/prime.exe
Fehlerhaft ist daran, daß ich noch nicht geprüft hatte, ob hinter cout tatsächlich eine Konsole hängt, oder es eine Dateiumleitung ist. Deswegen gibt "prime.exe > primes.txt" die Liste trotzdem auf den Bildschirm aus.Die prime.exe hatte ich auch vorbereitet mit dem Hintergedanken, daß dieses ständige "aber Java ist schneller" aufhört, bei Berechnungen war ist immer langsamer, das cout von Drinkumware und generell von c++ war damals noch nicht der Knaller. Kam aber nicht mehr dazu.