Foro de elhacker.net

Programación => Programación C/C++ => Mensaje iniciado por: X3R4CK3R en 28 Mayo 2014, 10:49 am



Título: C++ en linux, ¿cómo funciona?
Publicado por: X3R4CK3R en 28 Mayo 2014, 10:49 am
Buenas, resulta que desde hace tiempo, siempre que he programado C++ en Linux, he notado ciertas irregularidades con respecto a Windows, el ejemplo más simple es el siguiente:

Código
  1. #include <iostream>
  2. int main(int argc, char *argv[])
  3. {
  4.        sleep(1);
  5.        std::cout << "Hello world" << std::endl;
  6.        return 0;
  7. }

El cual mostraba el mensaje "Hello world" antes del sleep, y digo mostraba porque mientras escribía el hilo, he ido a compilar el código y me ha funcionado como debería (con gcc 4.6.4)

Con gcc 4.8.1 también funciona a la perfección:

Código
  1. #include <iostream>
  2. #include <thread>
  3. #include <chrono>
  4.  
  5. int main(int argc, char *argv[])
  6. {
  7.        std::this_thread::sleep_for(std::chrono::milliseconds(1000));
  8.        std::cout << "Hello world" << std::endl;
  9.        return 0;
  10. }


Tal vez sea que en aquel entonces tenía una versión de gcc más antigua que tenía este problema, o quizás que por aquel entonces compilaba con entornos como Geany, y ahora compilo por linea de comandos por SSH, que aunque lo que realmente importe sea el compilador y no el IDE, no sé, quizás algo tenía por ahí que no iba bien.



El caso es que siempre he programado bajo Windows y no le di demasiada importancia a lo anterior, pero ahora estoy programando un cliente y un servidor con sockets, y la aplicación del servidor la programo en un VPS con Ubuntu 12.04, por lo que este bug (o así al menos lo considero yo), esta vez tengo que quitármelo de en medio.

Yendo ya al grano, estoy haciendo un servidor con threads, y mientras que un hilo gestiona los paquetes recibido por los clientes, tengo un otro hilo de prueba que simplemente printea un punto con pausas de 30ms

Código
  1. Client::Client(MYSQL *m) : mysql(m), connected(true), acc_id(0)
  2. {
  3. mutex.lock();
  4. playingM.lock();
  5. thread = std::thread(&Client::loop, this);
  6. thread.detach();
  7.  
  8. coordT = std::thread(&Client::updateCoords, this);
  9. coordT.detach();
  10.  
  11. cout << "Client constructed." << endl;
  12. }
  13.  
  14.  
  15. void Client::updateCoords()
  16. {
  17. playingM.lock();
  18. while(true)
  19. {
  20. cout << ".";
  21. std::this_thread::sleep_for(std::chrono::milliseconds(30));
  22. }
  23. }
  24.  
  25. void Client::loop()
  26. {
  27. mutex.lock();
  28. while(connected)
  29. {
  30. sf::Packet packet;
  31. if(receive(packet) == sf::Socket::Done)
  32. {
  33. unsigned char pid;
  34. packet >> pid;
  35. if(pid!=0x00)
  36. cout << "Packet ID: " << (unsigned short)pid << " | from " << getRemoteAddress().toString() << " | (account.id: " << acc_id << ")." << endl;
  37. switch(pid)
  38. {
  39. case 0x00:
  40. {
  41. ...
  42. }
  43. break;
  44. case 0x01:
  45. {
  46. ...
  47. }
  48. break;
  49. case 0x02:
  50. {
  51. ...
  52. }
  53. break;
  54. case 0x03:
  55. {
  56. ...
  57. }
  58. break;
  59. case 0x04:
  60. {
  61. ...
  62. }
  63.  
  64. break;
  65. case 0x05:
  66. {
  67. ...
  68. }
  69. break;
  70. case 0x06:
  71. {
  72. ...
  73. playingM().unlock();
  74. ...
  75. }
  76. break;
  77. default:
  78. break;
  79. }
  80. }
  81. else
  82. {
  83. cout << "A client has disconnected (account.id: " << acc_id << ")." << endl;
  84. delete this;
  85. }
  86.    }
  87. }


Simplifico Client::loop() y aclaro que en cada case hay couts que son printeados inmediatamente en cuanto llega determinado paquete, mientras que el cout << "."; en Client::updateCoords() no printea nada hasta que lo hace cualquier otro cout en Client::loop(), es como si todos esos puntos se almacenasen en un buffer y de repente se printean todos juntos, cuando al tratarse de 2 hilos independientes, debería de printear por su propia cuenta.


Esto me parece más problema de la librería SFML que de Linux, aunque en Windows no tenga este problema, pero no descarto que la librería funcione de otra forma en Linux y por ello sea culpa de los threads de SFML. En un principio pensaba que era simplemente problema de Linux, ya que lo asocié a lo que expliqué al principio que me pasó hace tiempo, pero al ver que ésto ya no me ocurre, pienso que será cosa de SFML, pero no tengo nada claro.

EDITO: No hacer mucho caso a esto último, ya que los threads que uso no son de SFML sino de C++11, lapsus mental :X queda totalmente descartado SFML como causa del problema


Alguien puede aportar algún dato? Gracias y saludos

PD: Habré tardado como una hora en hacer este hilo, el título incluso lo considero incorrecto debido a mi cambio de opinión al ver que el problema del sleep ya no me ocurre, dudaba si postear en el foro de Linux o en el de C++, pero me decanto más por este, en fin, no sé, este proyecto me está estresando, así que publico el hilo ya y fin. XD


Título: Re: C++ en linux, ¿cómo funciona?
Publicado por: eferion en 28 Mayo 2014, 11:45 am
efectivamente, cuando tu llamas a cout, el mensaje no se envía directamente a la pantalla, sino que se almacena en el buffer de salida esperando a ser procesado.

Si quieres forzar el volcado del buffer en la pantalla puedes hacerlo con:

Código
  1. // opcion 1
  2. cout << "." << flush;
  3.  
  4. // opcion2
  5. cout << ".";
  6. cout.flush( );


Título: Re: C++ en linux, ¿cómo funciona?
Publicado por: X3R4CK3R en 28 Mayo 2014, 12:07 pm
Entiendo, pero a qué se debe que ese thread no lo procese inmediatamente? es debido a Linux?

Con respecto al flush, lo desconocía y me resuelve el problema perfectamente. Muchas gracias y saludos! :)


Título: Re: C++ en linux, ¿cómo funciona?
Publicado por: eferion en 28 Mayo 2014, 12:56 pm
C++ no dice en ningún momento que lo que envíes a cout tenga que salir inmediatamente por la pantalla.

cout escribe en el buffer de salida... luego ya es otro mecanismo el que se encarga de volcar esa información en la pantalla ( o eso o hasta que fuerces manualmente la salida ).

Normalmente este comportamiento pasa desapercibido porque en la estructura normal de los programas que tienes que hacer mientras aprendes, después de una salida pides datos al usuario, luego en ese momento sí que se fuerza el volcado a la pantalla... al ser un proceso tan rápido da la sensación de que el cout es automático.


Título: Re: C++ en linux, ¿cómo funciona?
Publicado por: X3R4CK3R en 28 Mayo 2014, 13:38 pm
Pues me informaré acerca de cómo funciona el buffer de cout y cuándo se vuelva en pantalla, gracias por el dato!