Estoy trabajando con algún código multiproceso para un proyecto de juego, y me cansé de ordenar el vómito stdout creado por dos hilos usando cout para depurar mensajes al mismo tiempo. Hice algunas investigaciones y miré una pared durante una hora o dos antes de pensar en "algo". El siguiente código usa SFML para mantener el tiempo y el subprocesamiento. SFML mutexes son simplemente secciones críticas envueltas en Windows.Thread safe cout technique. ¿Me estoy perdiendo de algo?
Header:
#include <SFML\System.hpp>
#include <iostream>
class OutputStreamHack
{
public:
OutputStreamHack();
~OutputStreamHack();
ostream& outputHijack(ostream &os);
private:
sf::Clock myRunTime;
sf::Mutex myMutex;
};
static OutputStream OUTHACK;
ostream& operator<<(ostream& os, const OutputStreamHack& inputValue);
Implementación:
#include <SFML\System.hpp>
#include <iostream>
#include "OutputStreamHack.h"
using namespace std;
OutputStreamHack::OutputStreamHack()
{
myMutex.Unlock();
myRunTime.Reset();
}
OutputStreamHack::~OutputStreamHack()
{
myMutex.Unlock();
myRunTime.Reset();
}
ostream& OutputStreamHack::outputHijack(ostream &os)
{
sf::Lock lock(myMutex);
os<<"<"<<myRunTime.GetElapsedTime()<<","<<GetCurrentThreadId()<<"> "<<flush;
return os;
}
ostream& operator<<(ostream& os, const OutputStreamHack& inputValue)
{
OUTHACK.outputHijack(os);
return os;
}
Uso:
cout<<OUTHACK<<val1<<val2<<val3....<<endl;
Ok, así que la forma en que esto funciona es a través de un operador de inserción sobrecargado que impone la seguridad hilo mediante el bloqueo de un iterador en un objeto estático, luego vaciar el búfer. Si entiendo el proceso correctamente (soy principalmente un programador autodidacta), cout procesa los elementos de su cadena de inserción desde el final hasta el principio, pasando una variable ostream por la cadena para cada elemento que se antepondrá a la secuencia. Una vez que llega al elemento OUTHACK, se llama al operador sobrecargado, el mutex está bloqueado y la secuencia se vacía.
He añadido algo de tiempo/información de depuración de id. De identificación a la salida para fines de verificación. Hasta ahora, mi prueba muestra que este método funciona. Tengo varios hilos golpeando con múltiples argumentos, y todo está saliendo en el orden correcto.
Por lo que he leído mientras investigaba este tema, la falta de seguridad de los hilos en cout parece ser un problema bastante común que las personas se topan al aventurarse en la programación de subprocesos. Lo que intento averiguar es si la técnica que estoy usando es una solución simple al problema, o si pienso que soy inteligente pero me falta algo importante.
En mi experiencia, la palabra inteligente cuando se usa para describir la programación es solo una palabra clave para el dolor retrasado. ¿Estoy trabajando en algo aquí, o simplemente persiguiendo pésimos hacks en círculos?
Gracias!
El hecho de que funcione es pura suerte. Solo la salida del tiempo y la identificación del hilo están protegidos por el mutex. Es perfectamente posible obtener otro subproceso entre 'OUTHACK' y' val1'. –
Considere escribir en un 'ostringstream' primero, luego volcar el contenido en una operación a' cout'. 'cout' generalmente será seguro para subprocesos, es solo que el bloqueo es obviamente por llamada, y cada operación' << 'es una llamada distinta .... De esta forma, no hay bloqueo adicional en el código de la aplicación, lo que solo puede reducir paralelismo. –
Relacionado: [¿Está sincronizado/seguro para subprocesos?] (Http://stackoverflow.com/questions/6374264/is-cout-synchronized-thread-safe/6374525#6374525) – legends2k