Bekanntermaßen schreibt man wie folgt mithilfe des Operators
std::cout
Intern wird alles, was man schreibt, in einem Stream-Puffer gespeichert und an die gewünschte Stelle geschrieben, wenn der Programmierer die Member-Funktion flush() aufruft oder der Puffer-Speicher voll ist und daher automatisch geleert wird.
Die Anweisung zur Leerung an einen Stream ist ganz einfach: Es genügt, std::endl in den Stream zu schreiben. Dies entspricht dem Schreiben von n in den Stream und dem anschließenden Leeren des Streams.
Zur Vereinfachung wird das Prinzip angewandt, dass beim Leeren eines Streams die aktuelle Mitteilung endet und eine neue Mitteilung beginnt, wie im Code unten zu sehen:
Um das Schreiben von Mitteilungen thread-sicher zu machen, sind die folgenden fünf Schritte auszuführen:
Der unten aufgeführte Code zeigt diese Schritte:
Das Monitoring der Leerung ist nicht so einfach, wie es scheint. Dazu muss die Funktion sync() des Puffer-Speichers des Streams aufgehoben werden. Listing C zeigt die Klasse basic_message_handler_log, in der alle diese Details verborgen sind. Man kann jedoch auch einfach eine eigene Protokoll-Klasse erstellen und die Funktion on_new_message überschreiben, die nach dem Schreiben einer neuen Mitteilung aufgerufen wird.
Der Trick mit den temporären Variablen
Nochmals zusammengefasst: Jeder Thread sollte seinen eigenen Stream S besitzen, wobei S eine Referenz auf U enthält (der einen thread-sicheren Zugang benötigt). Die einfachste Methode, jeden Thread mit einem eigenen Stream S zu versehen, liegt in der kontinuierlichen Verwendung temporärer Variablen. Diese sind grundsätzlich thread-sicher, da nur der jeweils zur Verwendung der Variablen eingesetzte Thread auf sie zugreift. Der Trick besteht in der Verwendung einer Funktion get_log, die intern eine statische Variable besitzt (den zugehörigen Stream U) und eine temporäre Variable (den Stream S) ausgibt, wie in Listing D dargestellt.
Die in Listing D gezeigte Methode ist nicht nur einfacher als die meisten alternativen Verfahren, sie ist auch am effizientesten. Ein Locking findet nur beim Leeren des Streams statt.
Lösung 1
Die Klasse thread_safe_log ist von basic_message_handler_log abgeleitet (wie in Listing C gezeigt) und überschreibt on_new_message, wobei in thread-sicherer Weise in das zugehörige Protokoll geschrieben wird. Dabei stellt sich die Frage, wo das kritische Section-Objekt untergebracht werden soll. Um thread_safe_log zu entkoppeln, muss eine weitere Klasse erstellt werden – internal_thread_safe_log –, die das Threading übernimmt.
Eine Funktion get_log sieht wie folgt aus:
Wichtig sind hierbei die Klassen internal_thread_safe_log und thread_safe_log, auf sie wird in diesem Artikel häufig Bezug genommen.
Listing E zeigt die oben genannten Methoden. Dabei ist zu beachten, dass der Code in Listing F zur korrekten Ausführung die in Listing F enthaltene Datei CriticalSection.h benötigt.
Es lässt sich nun Folgendes feststellen:
Wenn man diese Punkte nicht beachtet, kann dies schwerwiegende Folgen haben. Beispielsweise könnte U auf das deutsche Locale gesetzt sein (so dass 5.235 als 5,235 geschrieben wird). Würde S nun beim Drucken von Zahlen das standardmäßige Locale verwenden, würde die Ausgabe völlig falsch ausgelegt werden.
Listing E enthält außerdem einen Test. (Zu seiner Ausführung sind die Listings C und F erforderlich.) Der Test verwendet out.txt als zugehörigen Stream U. Er erstellt 200 konkurrierende Threads, wobei jeder der Threads 500 Mitteilungen folgender Art schreibt:
Wenn der Thread mit Index 10 die elfte Mitteilung schreibt, wird das Locale von U in Deutsch geändert. Alle nachfolgenden Mitteilungen schreiben dann „writing double 5,23“ statt „writing double 5.23“.
Beim Online-Gaming kommt es nicht nur auf das eigene Können an. Auch die technischen Voraussetzungen…
Fast jedes zweite Unternehmen bietet keinerlei Schulungen an. In den übrigen Betrieben profitieren oft nur…
Huawei stellt auf der Connect Europe 2024 in Paris mit Xinghe Intelligent Network eine erweiterte…
Höchste Zeit für eine schnelle Kupfer-Glas-Migration. Bis 2030 soll in Deutschland Glasfaser flächendeckend ausgerollt sein.
Schon im April 2025 soll Android 16 den Status Plattformstabilität erreichen. Entwicklern gibt Google danach…
Die Hintermänner setzen KI-Chatbot-Tools als Köder ein. Opfer fangen sich den Infostealer JarkaStealer ein.