Eigenes std::cout
-
Also erst mal der Code...
class my_cout { private: const HANDLE free_out; public: my_cout (void) : free_out (CreateEvent (NULL, false, true, NULL)) {}; void operator << (const std::ostream &ausgabe) { WaitForSingleObject (free_out, INFINITE); std::cout << ausgabe; SetEvent (free_out); }; }; my_out My_out;
My_out << "blabla" << var1 << "asd" << var2 << endl;
Da der Sinn dahinter vll nicht gleich klar wird: Das std::cout ist nicht threadsicher - also schlimmes passiert ja nicht, aber die verschiedenen Ausgaben werden alle kreuz und quer gemischt... Ich weiß - cirtical-section usw - geht auch erst mal nur um den Sinn des ganzen... Ich wäre sehr dankebar für Hilfe... Danke!
-
ja und was ist deine frage
-
Also zuerstmal sollte ein operator<< eine Referenz auf den Stream zurückgeben, bzw. wie du das in deinem Fall aufrufen willst, ist mir eh nicht ganz klar, da deine Klasse nicht von ostream erbt, kann da niemand seinen output einfach reinschieben.
Ich würd vermutlich zuerst einen eigenen Thread starten, der die Ausgabe verwaltet und sortiert. Jeder Thread der dann neu gestartet wird bekommt, wenn er denn unbedingt Ausgaben machen muss, ein eigenes streamobjekt zugewiesen, und der "Verwalter"-Stream sorgt dafür, dass die Ausgabe ordentlich getrennt/sortiert ausgegeben wird, z.B. indem er den Inhalt eines streambuffers an cout weitergibt, sobald einer der Threadstreams geflusht wird.
Die vermutlich sinnigere Alternative ist, dass die einzelnen Threads keine eigenständige Ausgabe machen dürfen. Stichwort MVC: ein Thread ist für die Interaktion mit dem Nutzer verantwortlich, deshalb hat nur der eine Thread was an cout zu schreiben. Die anderen Thread verschicken untereinander Signale, so dass der eine Thread weiß, wannn er eine Message anfordern kann, die er dann auf die Ausgabe legt.
-
Die Frage ist, warum es nicht geht... sry - hatte ich im Eifer des gefechts vergessen, mit hin zu schreiben... Er will einfach das rechts nicht umwandeln... sagt mir dann ständig, dass er const char [4] oder wie viel zeichen auch immer, nicht umwandeln kann...
pumuckl: ok - der operator gibt jz ne const ref. auf den stream zurück...
warum muss die klasse den von ostream erben? dafür hab ich ja das "normale" std::cout... oder seh ich da was falsch?"Die vermutlich sinnigere Alternative ist, dass die einzelnen Threads keine eigenständige Ausgabe machen dürfen" Naja... Da hab ich auch schon lang drüber nachgedacht - aber ist in meinem Fall nicht so einfach machbar... Hab ich wo am Anfang auch nicht ordentlich drüber nachgedacht - und das jz noch zu ändenr wäre der Horror - und hab ich eigtl auch nicht vor ^^
-
Dein Versuch der Ausgabe funktioniert deshalb nicht, weil du nur einen operator<< (ostream&) definiert hast - Sprich du kannst in my_out nur ostreams packen, keine const char* s wie bei dir vorgesehen. Du müsstest, um allgemeine Ausgabe machen zu können, deine KLasse von ostream abeliten und entsprechend mit dem Streambuf usw. rumwerkeln - viel Spaß dabei ^^
-
hmm... verdammt ^^ hatte mir das eigtl genau so einfach vorgestellt ^^ naja - ma gucken, ob ich das hinbekomm (würd ja ma denken, dass es jz geht aber kanns gerad ne ausprobieren, weil noch millionen andere fehler im projekt sind, weil ich was ändern musste -.-)... danke erst mal :>
bb
-
pumuckl schrieb:
Dein Versuch der Ausgabe funktioniert deshalb nicht, weil du nur einen operator<< (ostream&) definiert hast - Sprich du kannst in my_out nur ostreams packen, keine const char* s wie bei dir vorgesehen. Du müsstest, um allgemeine Ausgabe machen zu können, deine KLasse von ostream abeliten und entsprechend mit dem Streambuf usw. rumwerkeln - viel Spaß dabei ^^
Dadurch kommt aber keine echte Threadsicherheit zustande, da die Standard-op<<s nicht threadsicher sind (die können ihre Ausgabe ja in mehrere Teile zerlegt tätigen).
Man muss sich also wirklich hier die Mühe machen und alles threadsicher implementieren. Bevor du das machst könntest du aber auch einfach mal schauen ob du wirklich Compiler-unabhängig sein musst und wenn nicht schauen ob dein Compiler vielleicht eine Option bietet mit der die Standardbibliothek thread-sicher wird.
Beim VC geht das zum Beispiel.
-
'Beim VC geht das zum Bsp.'
Oo
verdammt - hätte man davor auch ma drauf kommen können - aber naja... zu spät ^^
-
Sooo... Nach einiger Zeit (wo ich erst mal nen paar andere Fehler beseitigen musste) jetzt ein neuer (nicht sehr viel besserer) Vorschlag und verbunden damit gleich ein Hilferuf nach Links oder Erklärungen oder so was ^^
ausgabe.hpp:
#include <iostream> #include "threads.hpp" namespace my { class my_cout : private threads_simple, public std::ostream { public: my_cout (void); const std::ostream& operator << (const std::ostream &ausgabe); }; };
ausgabe.cpp:
my::my_cout::my_cout (void) : threads_simple ()//, std::ostream (std::cout) {}; const std::ostream& my::my_cout::operator << (const std::ostream &ausgabe) { lock (); //aus threads_simple std::cout << ausgabe; //"normale" ausgabe unlock (); //aus threads_simple return ausgabe; };
Die Fehlermeldung (aber wie muss man std::ostream initiieren? (so, wie es da oben auskommentiert steht, hab ich es auch schon probiert, aber naja...
):
f:\c\dahlia\konsole\dahlia_konsole_new\data\ausgabe.cpp(4) : error C2512: 'std::basic_ostream<_Elem,_Traits>': Kein geeigneter Standardkonstruktor verfügbar with [ _Elem=char, _Traits=std::char_traits<char> ]
Das würde das Problem vermutlich noch lange nicht lösen aber mich hoffentlich ein Stück weiterbringen... Mein eigtl Prob ist eigtl, dass ich bei Streams nur raten kann - ich weiß, dass es sie gibt und meistens, welche ich benutzen muss - aber hier müsste ich eben ma bissl mehr wissen ^^
Bye und danke scho mal : >
-
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... -.-