Eigenes std::cout
-
Der Ostream-Ctor erwartet einen Streambuffer:
http://www.cplusplus.com/reference/iostream/ostream/ostream.html
-
Nur mal am Rande, du hasst ne eigenwillige Auffassung was threadsicherheit bedeutet.
Threadsicher bedeutet nicht, das die bib dir deine eingaben und ausgaben sortiert, sondern nur dass Du ueberhaupt in der lage sein kannst, das zeugs so zusammenzubauen wie du willst.Die STL und damit die iostreams sollten schon threadsicher sein, welche Impl nutzt du denn ? (ja bei MS gabs mal ne version die war ned 100% threadsicher, das wurde aber gifixt).
Wenn du wirklich das zeugs geordnet ausgeben willst, wuerd ich deine Threads in ihren eigenen Buffer(stringstream z.b.) schreibenlassen, und dann nur einen ! thread das zeugs aus den buffern zusammensammeln lassen und nach cout ueberfuehren. Wann was zusammengesammelt werden kann, solltest dann mit Evenst signalen oder aehnliche sachen loesen.
Ciao ...
-
RHBaum schrieb:
Wenn du wirklich das zeugs geordnet ausgeben willst, wuerd ich deine Threads in ihren eigenen Buffer(stringstream z.b.) schreibenlassen, und dann nur einen ! thread das zeugs aus den buffern zusammensammeln lassen und nach cout ueberfuehren. Wann was zusammengesammelt werden kann, solltest dann mit Evenst signalen oder aehnliche sachen loesen.
Genau das hatte ich ja schon vorgeschlagen, war aber abgelehnt worden weil die Aenderung zu viel Arbeit bedeutet haette. Allerdings: ein verungluecktes Design nachtraeglich zu aendern ist eigentlich nie soviel Arbeit und Stress, wie mit dem verunglueckten Desgin zu leben und darauf noch weiter aufzubauen
-
"Threadsicher bedeutet nicht, das die bib dir deine eingaben und ausgaben sortiert, sondern nur dass Du ueberhaupt in der lage sein kannst, das zeugs so zusammenzubauen wie du willst."
Ok - das is mir klar - dann wars eben ein wenig unglücklich formuliert...
Die Ausgabe nur über einen Thread und mit Events zu lösen hilft mir aber auch nicht bei dem Operator << - dann schreib ich eben so was in der Art:my::cout << "asd" << "sdf" << my_int_varialbe << std::endl;
und hau es eben nicht gleich in den std::cout - Buffer sondern erst in nen Buffer eines anderen Threads, setz nen Event und der Thread gibt es dann eben aus... Die Frage ist eben, ob das irgendwelche Vorteile hat - ich glaube nämlich nicht - will da ja auch nicht so tun, als ob ich es besser wüsste, aber das Problem an sich besteht so ja immer noch -.-
"Die STL und damit die iostreams sollten schon threadsicher sein, welche Impl nutzt du denn?"
std::cout, std::cerr, std::clog nutze ich - threadsicher an sich stimmt schon - nur leider wird die Ausgabe dann eben immer gemischt (zumindest bei cout und ich denke auch bei clog - nur bei cerr _sollte_ es so gehen)Danke schon ma
PS: std::stringstream geht aber anscheind auch nicht für const char, oder hab ich da was falsch verstanden?
//EDIT
so hab ichs jz atm ma probiert - und das einzige, was jz nicht geht, ist SOCKET, so weit ich das überblicke ^^class my_cerr : private threads_simple, public std::stringstream { public: my_cerr (void) : threads_simple (), std::stringstream () {}; const std::stringstream& operator << (const std::stringstream &ausgabe) { lock (); std::cerr << ausgabe; //denkt euch hier halt was anderes hin... unlock (); return ausgabe; }; };
-
Wenn nur ein Thread Ausgabeanweisungen entgegennehmen kann, dann setzt du einen Lock, nimmst den kompletten Krempel den du ausgeben sollst entgegen, schiebst ihn in std::cout, flushst std::cout und löst den Lock auf. Alle anderen Threads die was ausgeben wollen müssen derweil auf den Lock (und damit auf den flush) warten und niemand kann dir was dazwischen spucken. Oder hab ich da was übersehen? Was du dann natürlich nicht machen kannst sind Aufrufe á la
my::out << bla << "blubb" << 123;
, weil das aufeinanderfolgende Aufrufe der op<< sind und der Thread nicht wissen kann, ob er zwischendurch den Lock aufheben soll. Zwei Möglichkeiten:
- Du setzt innerhalb des Threads den Lock nd musst deshalb immer die komplette Ausgabe in einem Stück rübergeben (vorher mit std::stringstream o.ä. zusammenschrauben)
- Du forderst den Lock vor einer Ausgabe von außerhalb an, kannst danach alles in Einzelteilen mit mehreren op<< an den Stream schicken und gibst den Lock dann jedes Mal wieder frei. Das händisch zu machen ist Fehleranfällig, irgendwann vergisst man sonst die Freigabe und nach ner weile wartet alles auf de vergessenen Lock. Deshalb würde ich es mit RAII machen: Baue eine Klasse, die im Ctor einen Lock auf den Ausgabe-Thread anfordert und sich dann eine Referenz auf dessen Ausgabestream besorgt. Im Dtor fulshst du den Stream und gibst den Lock wieder frei. Die Klasse könnte von std::ostream ableiden und dessen Methoden stumpf an den ostream des Ausgabethreads weiterleiten.
-
Genau das ist mein Problem : <
Ich will ne jedes mal 3 Zeilen für ne Ausgabe schreiben... (á la WaitForSingleObject (...), std::cout << "blubb" << std::endl, SetEvent (...))
ich wollte mir eben den Aufwand ersparen und habs jz zwar hinbekommen (mehr oder weniger schön - wird mir aber wahrscheinlich trotzdem nicht so viel nutzen - werd ich dann beim nächsten Test sehen, wenn ich die 15 (mir unbegreiflichen) Linker-Fehler wegbekomm habe - ich sollte mir angewöhnen, nicht nur einmal in der Woche zu compilieren und nicht immer alles auf einmal zu ändern - naja...byebye
-
Es gehn auch 2 Zeilen
class myout : public std::ostream { static Lock thelock; //ein Lock fuer alle std::ostream& os; //verbiete Kopien myout& operator=(myout const&); myout(myout const&); public: explicit myout(std::ostream& os = std::cout) : os(os) { thelock.lock(); //nur ein Objekt zur Zeit kann den Lock bekommen } ~myout() { os.flush(); thelock.unlock(); } //ostream-member ueberschreiben, einfach alles an den referenz-stream weiterleiten ostream& operator<< (double& val ) { os << val; return *this; } /* usw. */ }; ///////Code irgendwo...//////////////// void foo() { /* Berechnungen */ { //interner scope-Block myout out; //holt sich den lock, alle anderen muessen warten! out << bla; out << "bubb" << 126 << std::endl; int a = 25; // weitere Berechnungen, andere threads warten evtl weiter auf den lock out << a; } //scope ende, out wird zerstoert und der lock freigegeben //Genug der Spielerei, output in 2 Zeilen: myout logfileout(std::ofstream("logfile.txt")); //wieder lock geholt logfileout << "foo beendet"; } //logfileout geht aus dem scope -> lock wieder freigegeben
-
Danke - sehr schöner Vorschlag, find ich
Leider gibts da nen Fehler (nicht nur meiner Meinung - das sieht auch der Compiler nicht anders ^^):class myout : public std::ostream { /*...*/ explicit myout(std::ostream& os = std::cout) : os(os) //std::ostream (????) { /**/ }; };
Im CTor muss (müsste?! ^^) ja auch std::ostream initialisiert werden, wenn davon geerbt wird... Und das ist ja der Schritt, über den ich die ganze Zeit schon nur rumraten kann
danke trotzdem und hoffentlich auch danke noch mal ;o)byebye
-
ui. Offenbar hat hier keiner verstanden, wie die C++-IO-Streams funktionieren
.
Wenn du ein "Threadsafe" std::cout haben willst, dann solltest du den stream_buf Threadsafe machen
Tipp: Schau dir mal Boost.IOStreams an. Das ist ganz praktisch, wenn man die IO-Streams erweitern will.
-
RHBaum schrieb:
Nur mal am Rande, du hasst ne eigenwillige Auffassung was threadsicherheit bedeutet.
Threadsicher bedeutet nicht, das die bib dir deine eingaben und ausgaben sortiert, sondern nur dass Du ueberhaupt in der lage sein kannst, das zeugs so zusammenzubauen wie du willst.Die STL und damit die iostreams sollten schon threadsicher sein, welche Impl nutzt du denn ? (ja bei MS gabs mal ne version die war ned 100% threadsicher, das wurde aber gifixt).
Aus reiner Neugier: inwiefern ist die STL denn threadsafe? kann ich davon ausgehen, dass ein push_back() atomar ist? Und falls ja: wuerde sich das nicht sehr negativ auf die Performance im Singlethreaded Betrieb auswirken?
-
warum sollte ein push_back nicht atomar sein?
alles, was ich wissen will, ist doch, wie zum teufel der ctor von "std::ostream" ist - ich versteh zwar, dass was in der msdn steht usw., aber iwie kommt jedes mal, wenn ich iwas von ostream erben lasse wieder nen 'ungültiger ctor'-Fehler... -.-
-
leg dir mal n Bookmark auf die C++-Referenz oder auf cppreference.com oder eine andre Referenz deiner Wahl, da steht sowas drin
und ein nicht-atomares vector::push_back wuerde ich naiverweise erwarten, wenn vec.size() == vec.capacity =>
- neuen Speicher alokieren
- alte Werte rueberkopieren
- neuen Wert hinten anhaengenWuerd mich schon wundern, wenn das in jeder STL-Implementierung standardmaessig threadsafe (ohne overhead durch locks/mutexe/critical sections) ist, aber ich lass mich gern ueberraschen
-
-
"- neuen Speicher alokieren"
-
"- alte Werte rueberkopieren"
-
"- neuen Wert hinten anhaengen"
-
ok - hier könnte es zutreffen...
-
soll er noch im 'alten' Speicher rumoperieren? ich glaube nicht...
-
max. hier - aber das würde ich für komisch halten - warum sollte man mit so ner einfachen (und schnellen) operation denn warten?
Naja - wie auch immer...
'cppreference.com' kenn ich - nur leider bin ich entweder zu doof oder blind oder wie auch immer -.-
-
-
nimm boost::format^^
-
unskilled schrieb:
-
"- neuen Speicher alokieren"
-
"- alte Werte rueberkopieren"
-
"- neuen Wert hinten anhaengen"
-
ok - hier könnte es zutreffen...
-
soll er noch im 'alten' Speicher rumoperieren? ich glaube nicht...
-
max. hier - aber das würde ich für komisch halten - warum sollte man mit so ner einfachen (und schnellen) operation denn warten?
Ich glaub du hast Pumuckl nicht verstanden... damit push_back atomar ist, duerfen diese 3 Aktionen nicht unterbrochen werden, wenn bei einem Vektor hinten was angehaengt wird und dabei neuer Speicher reserviert werden muss. D.h. man braeuchte dazu einen Lock. Aber das wuerde einen ziemlichen Performance-Unterschied ausmachen fuer Leute, die std::vector nur im singlehtreaded-betrieb verwenden. genauso braeuchte es fuer std::list<>::push_back einen Lock, damit nicht mitten im Zeiger-Umbiegen ein anderer Thread dazwischenfunkt.
Jedenfalls wuerds mich interessieren was RHBaum gemeint hat mit "STL ist threadsafe".'cppreference.com' kenn ich - nur leider bin ich entweder zu doof oder blind oder wie auch immer -.-
Auf cppreference.com steht das auch nicht
Aber auf http://cplusplus.com/reference/ ==> du musst einen Stream_buf uebergeben.
-
-
Zu dem ersten - ja... Und das war so gemeint, dass der erste Schritt _VLL_ nicht mit dazugehört - der Rest aber mit Sicherheit atomar ist...
Zu dem Link: Danke - ma gucken, ob mich das weiterbringt - ich versuch ma, mich ne ganz so doof anzustellen und schreib dann später noch mal ;o)
Ciao
-
unskilled schrieb:
Zu dem ersten - ja... Und das war so gemeint, dass der erste Schritt _VLL_ nicht mit dazugehört - der Rest aber mit Sicherheit atomar ist...
Die Werte zu kopieren ist (ohne externe Hilfsmittel) sicher nicht atomar
Und Wertzuweisung (Schritt 3) auch nur (wenn ueberhaupt) fuer Maschinendatentypen. Ausserdem muessten die 3 Aktionen ZUSAMMEN atomar sein, damit push_back atomar ist
EDIT: um mir meine Frage mal selbst zu beantworten
http://www.sgi.com/tech/stl/thread_safety.html
http://gcc.gnu.org/onlinedocs/libstdc++/manual/bk01pt01ch03s05.html#manual.intro.using.concurrency.thread_safety
-
hmm... Mein nächster Versuch... (wahrscheinlich denkt jz auch der letzte, dass ich doof bin - aber nunja... ^^) (die locks / unlocks dann sind ja kein prob mehr - nur erst ma das prinzip...)
class hlper { protected: std::filebuf FB; public: hlper (std::string P) { FB.open (P.c_str (), std::ios::out); }; }; class my_cout : private hlper, public std::ostream { private: std::ostream os; public: my_cout (std::string Para) : hlper (Para), std::ostream (&(FB)), os (&(FB)) {}; const std::ostream& operator << (const std::stringstream &ausgabe) { os << ausgabe; return *this; }; const std::ostream& operator << (const std::ostream &ausgabe) { os << ausgabe; return ausgabe; }; const std::ostream& operator << (SOCKET ausgabe) { os << static_cast <int> (ausgabe); return *this; }; };
Eigtl geht das ganze auch schon ganz gut (mal abgesehen davon, dass es echt nicht schön ist - aber mein Projekt kann ja nicht nur hübsche Klassen haben ;o) ):
namespace my { my_cout cout ("log_cout.txt"); } using my::cout; SOCKET socket; cout << socket; //geht cout << socket << "...."; //geht nicht mehr - obwohl ich dachte, dass das return *this; richtig wäre -.-
Danke
-
class my_cout : private hlper, public std::ostream { private: std::ostream os; public: my_cout (std::string Para) : hlper (Para), std::ostream (&(FB)), os (&(FB)) {}; const my_cout& operator << (const std::stringstream &ausgabe) { os << ausgabe; return *this; }; const my_cout& operator << (const std::ostream &ausgabe) { os << ausgabe; return ausgabe; }; const my_cout& operator << (SOCKET ausgabe) { os << static_cast <int> (ausgabe); return *this; }; };
-
Ach ich Trottel ^^ Danke :>
btw: das const war auch falsch ^^ weil ich den operator ja nicht als const deklarieren kann...