Thread Safe Cout Technik. Fehle ich etwas?

8

Ich arbeite mit etwas Multithread-Code für ein Spielprojekt und war ein wenig müde, das durch zwei Threads erzeugte stdout-Erbrechen zu durchforsten, das cout zum gleichzeitigen Debuggen von Nachrichten verwendet. Ich recherchierte und starrte für ein oder zwei Stunden auf eine Wand, bevor ich "etwas" erfand. Der folgende Code verwendet SFML zum Zeithalten und Threading. SFML-Mutexe werden nur kritische Bereiche in Windows umschlossen.

Kopfzeile:

%Vor%

Implementierung:

%Vor%

Verwendung:

%Vor%

Ok, also funktioniert das über einen überladenen Insertionsoperator, der die Threadsicherheit durch Sperren eines Iterators in einem statischen Objekt und anschließendes Leeren des Puffers erzwingt. Wenn ich den Prozess richtig verstehe (ich bin meistens ein autodidaktischer Programmierer), verarbeitet cout Elemente seiner Einfügekette vom Ende zum Anfang und leitet eine ostream Variable über die Kette für jedes Element, das dem Stream vorangestellt wird. Sobald es das OUTHACK-Element erreicht, wird der überladene Operator aufgerufen, der Mutex wird gesperrt und der Stream wird geleert.

Ich habe einige Zeit / Thread-ID-Debugging-Informationen zu Verifizierungszwecken zur Ausgabe hinzugefügt. Bis jetzt, meine Tests zeigen, dass diese Methode funktioniert. Ich habe mehrere Threads mit mehreren Argumenten, und alles kommt in der richtigen Reihenfolge heraus.

Nach dem, was ich gelesen habe, während ich dieses Problem untersucht habe, scheint ein Mangel an Thread-Sicherheit in cout ein ziemlich häufiges Problem zu sein, auf das Leute stoßen, während sie sich in die Thread-Programmierung wagen. Ich versuche herauszufinden, ob die Technik, die ich verwende, eine einfache Lösung für das Problem ist, oder dass ich denke, dass ich clever bin, aber etwas Wichtiges vermisse.

Nach meiner Erfahrung ist das Wort "clever", wenn es zum Beschreiben der Programmierung verwendet wird, nur ein Codewort für verspäteten Schmerz. Bin ich hier auf etwas, oder jage nur lausige Hacks im Kreis?

Danke!

    
Chris 02.03.2012, 02:21
quelle

1 Antwort

19

Was hier nicht threadsafe ist, ist nicht cout per se. Es ruft nacheinander zwei Funktionsaufrufe auf. std::cout << a << b entspricht in etwa dem Aufruf von operator<<(std::cout, a) gefolgt von operator<<(std::cout, b) . Der Aufruf von zwei Funktionen in der Reihenfolge garantiert nicht, dass sie atomar ausgeführt werden.

Wie es ist, ist nur die Ausgabe der Zeit und der Thread-ID durch den Mutex geschützt. Es ist durchaus möglich, einen weiteren Thread-Sneak zwischen der Einfügung von OUTHACK und val1 zu erhalten, da die Sperre nicht mehr aufrechterhalten wird, nachdem OUTHACK eingefügt wurde.

Sie können operator<< für Ihr OutputStreamHack return nach Wert ein Objekt haben, das im Destruktor freigeschaltet wird. Da Provisorien bis zum Ende jedes vollständigen Ausdrucks existieren, würde der Code die Sperre "bis zum Semikolon" halten. Da jedoch Kopien beteiligt sein können, könnte dies ohne einen Verschiebungskonstruktor (oder einen benutzerdefinierten Kopierkonstruktor in C ++ 03, ähnlich wie auto_ptr 's keuchen ) problematisch sein.

Eine andere Option ist die Verwendung der vorhandenen Thread-Sicherheit von cout (garantiert durch die Sprache in C ++ 11, aber viele Implementierungen waren vorher threadsafe). Machen Sie ein Objekt, das alles in ein std::stringstream -Member streamt und schreiben Sie es dann auf einmal auf, wenn es zerstört ist.

%Vor%     
R. Martinho Fernandes 02.03.2012, 02:30
quelle