• Publicidad

Problema con threads al hacer un servidor

¿Ya sabes lo que es una referencia? Has progresado, el nível básico es cosa del pasado y ahora estás listo para el siguiente nivel.

Problema con threads al hacer un servidor

Notapor creating021 » 2007-06-25 18:33 @814

Hola.
Siguiendo el recipiente 17.14 de Perl CookBook (2nd edition) de hacer un servidor multitasking (uso Sockets y no IO::Sockets por algunos problemas y limitaciones).

La cosa es que al usarlo ciertas veces el servidor muere... sin dar error alguno.

El código es muy similar al del libro (la parte de thread es 100% igual) y me pregunto sí el problemas es threads y si es bueno hacerlo con threads.pm o si hay mejor forma de hacerlo (sin usar POE).

Gracias.
Expect the worst, is it the least you can do?
Avatar de Usuario
creating021
Perlero frecuente
Perlero frecuente
 
Mensajes: 595
Registrado: 2006-02-23 16:17 @720
Ubicación: Frente al monitor

Publicidad

Notapor creating021 » 2007-06-25 18:44 @822

El prolbemas está en perldoc (al parecer es esto)

WARNINGS
A thread exited while %d other threads were still running

A thread (not necessarily the main thread) exited while there were still other threads running. Usually it's a good idea to first collect the return values of the created threads by joining them, and only then exit from the main thread.


No se que hacer...
Expect the worst, is it the least you can do?
Avatar de Usuario
creating021
Perlero frecuente
Perlero frecuente
 
Mensajes: 595
Registrado: 2006-02-23 16:17 @720
Ubicación: Frente al monitor

Notapor Perl user » 2007-06-25 21:31 @938

Que tal,

Bueno en primera instancia, no has publicado nada de código como para saber qué está REALMENTE mal, pero... te puedo casi asegurar que efectivamente SI es el thread principal el que está muriendo antes que los demás terminen, y esto es un comportamiento érroneo (es decir, de mal diseño) si los otros threads que están vivos no son daemons.

Lo que te dice el manual es correcto, ya que tu thread principal (o el que los lanzó a todos los que quedan vivos) de alguna manera tiene que esperar a que los otros terminen por medio de join().

El uso de threads.pm está realmente vetado puesto que baja considerablemente el performance del intérprete de Perl y también hace que muchos módulos (principalmente los de XS) fallen o tengan comportamientos indefinidos. Otra cosa importante es que multitasking es diferente a multi-threaded, hay muchas técnicas para realizar aplicaciones multitareas asíncronas, y ello implica incluso el uso de Poll/epoll, select, kselect, etc (Qué son algunos mecanismos de los que usa POE) y son muy eficientes puesto que los eventos son proporcionados directamente por el kernel.

Lo que te recomiendo, si todavía quieres algo como threads.pm, es que uses el pragma 'forks' que implementa todo lo que te ofrece threads.pm, pero utilizando como el nombre lo indica, la llamada al sistema fork.

Lamentablemente el manejo de múltiples threads en Perl sigue teniendo un mal desarrollo y todavía está muy verde, y es por eso que se recomienda evitar su uso, los ithreads existen con el propósito de emular fork() y afines en sistemas Win32, pero no para generar aplicaciones multi-thread en producción.

Así que bueno, la recomendación es: usa forks o usa POE; POE sigue siendo la mejor opción, a menos que REALMEMENTE tu implementación necesite de un thread/fork.

Saludos,
Marco A. Manzo
[email protected]
http://www.unixmonkeys.com/amnesiac/
Perl Programming Language
Perl user
Maestro honorario
Maestro honorario
 
Mensajes: 271
Registrado: 2004-11-03 21:11 @924

Notapor creating021 » 2007-06-30 16:28 @728

Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
use threads;
use IO::Socket;

my $listen = IO::Socket::INET->new(
   LocalPort => 80,
   ReuseAddr => 1,
   Listen => 10,
);

sub handle_connection {
  my $socket = shift;
  #código y más código...
}

while(my $socket = $listen->accept){
  async(\&handle_connection, $socket)->detach;
}
Coloreado en 0.004 segundos, usando GeSHi 1.0.8.4


Bueno, el código es similar (como ya dije, estoy usando Socket y no IO::Socket).

Y según mi libro (Computer Science & Perl Programming) no es una buena idea por el tema de ports a otros OS (como Win32 y preOS X).

Supongo que lo mejor es hacerlo con fork... pero me surge una duda:
Viendo el código de Perl CoockBook (nueva mente) noto que usa POSIX.
¿Si lo hago así, sería portable a Win32 y OS X?

Gracias por los comentarios.
Expect the worst, is it the least you can do?
Avatar de Usuario
creating021
Perlero frecuente
Perlero frecuente
 
Mensajes: 595
Registrado: 2006-02-23 16:17 @720
Ubicación: Frente al monitor

Notapor Perl user » 2007-07-01 12:51 @577

creating021 escribiste:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
use threads;
use IO::Socket;

my $listen = IO::Socket::INET->new(
   LocalPort => 80,
   ReuseAddr => 1,
   Listen => 10,
);

sub handle_connection {
  my $socket = shift;
  #código y más código...
}

while(my $socket = $listen->accept){
  async(\&handle_connection, $socket)->detach;
}
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4


Bueno, el código es similar (como ya dije, estoy usando Socket y no IO::Socket).

Y según mi libro (Computer Science & Perl Programming) no es una buena idea por el tema de ports a otros OS (como Win32 y preOS X).

Supongo que lo mejor es hacerlo con fork... pero me surge una duda:
Viendo el código de Perl CoockBook (nueva mente) noto que usa POSIX.
¿Si lo hago así, sería portable a Win32 y OS X?

Gracias por los comentarios.


Mira... la elección de usar Socket e IO::Socket es realmente sin importancia, no creo que estés desarrollando una aplicación de misión crítica como para que el 0.02% de overhead de IO:Socket sobre Socket tenga alguna importancia. IO::Socket es solo un wrapper sobre Socket, el cual es a su vez un wrapper sobre bsd sockets

Y según tu libro, el cuál también tengo, no es buena idea qué? Si hablas de una llamada fork(), fork es accesible en cualquier sistema tipo UNIX, y está implementado para sistemas que no lo soportan por medio de cuestiones como ithreads (por eso es costoso...), así que... ESO es portable.

Y no sé a qué recipe del Perl Cookbook te refieres, pero yo hablaba de forks, el pragma, no fork() la llamada al sistema. Lee nuevamente mi comentario y verifica por qué mencioné forks (como pragma).

Otra alternativa es utilizar Coroutines, alternativa muy buena para eventos asíncronos, que no requiere de sincronización ni locking.

Saludos,
Marco A. Manzo
[email protected]
http://www.unixmonkeys.com/amnesiac/
Perl Programming Language
Perl user
Maestro honorario
Maestro honorario
 
Mensajes: 271
Registrado: 2004-11-03 21:11 @924

Notapor creating021 » 2007-07-01 14:42 @654

Perl_user escribiste:Mira... la elección de usar Socket e IO::Socket es realmente sin importancia, no creo que estés desarrollando una aplicación de misión crítica como para que el 0.02% de overhead de IO:Socket sobre Socket tenga alguna importancia. IO::Socket es solo un wrapper sobre Socket, el cual es a su vez un wrapper sobre bsd sockets


Solo quería aclarar las diferencias y tienes mucha razón en esto, realment no importa.

Perl_user escribiste:Y según tu libro, el cuál también tengo, no es buena idea qué? Si hablas de una llamada fork(), fork es accesible en cualquier sistema tipo UNIX, y está implementado para sistemas que no lo soportan por medio de cuestiones como ithreads (por eso es costoso...), así que... ESO es portable.


No, no fork, Threads.pm en Perl 5.005_03... no lo aclaré (notese, Threads y no treads).
La pregunta era para POSIX, pero ya que no lo uso, no importa... de todos modos encontré (ya no tengo el link) lo que si y no era soportado en diferentes OS las funciones de POSIX.

Perl_user escribiste:Y no sé a qué recipe del Perl Cookbook te refieres, pero yo hablaba de forks, el pragma, no fork() la llamada al sistema. Lee nuevamente mi comentario y verifica por qué mencioné forks (como pragma).


17.algo, uno que usa fork() y POSIX...
Resulta que solucioné el problema con fork(), sin POSIX y actualmente funciona sin problema.

Cualquier módulo (de los que has nombrados) logran la facilidad de programar el servidor con muy buena calidad.

Muchas gracias por todo esto, me ha sido de mucha ayuda.
Expect the worst, is it the least you can do?
Avatar de Usuario
creating021
Perlero frecuente
Perlero frecuente
 
Mensajes: 595
Registrado: 2006-02-23 16:17 @720
Ubicación: Frente al monitor

Notapor Moraita » 2009-03-24 06:31 @313

Hola expertos.

He decidido responder a este mensaje que he encontrado en el foro, ya que el título sería igual que el que yo pondría y con el fin de evitar duplicación de problemas respecto al mismo tema, pues respondo a este mensaje del 2006. Si hago mal en este sentido, hacédmelo saber. ¡Gracias!

Por otro lado, veo que ese de nivel "intermedio". Quiero pedir disculpas de antemano por mi atrevimiento. Siendo una novata, no sé si tengo derecho a escribir fuera del "básico".

Bien, expondré mi duda respecto a utilizar un servidor como un thread, que con la sobredosis de información que me he metido no acabo de entender el porqué de la cuestión.

Aquí está el código básico que engloba dos threads trabajando en paralelo. Uno inicia un servidor que recibirá señales (requets) y el otro thread que trabajará en paralelo realizando x tareas tras haber detectado que se recibieron señales en el servidor.

Para implementar el servidor estoy utilizando HTTP::Server::Simple por imposición. No puedo elegir aquí.
Ambos threads comparte la variable "signal" que será su modo de comunicación.


Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
#!/apps/essbase9/perl/bin/perl

#declare packages
use threads ();
use threads::shared ();
my $signal : shared = 0;
my $countSignals = 0;
my $countExecutions = 0;

my @http_params = '';
my @partition_params = '';

my $timestamp = `date '+%d/%m/%y%H%M%S'`;

package MyWebServer;
use base qw(HTTP::Server::Simple::CGI);
sub handle_request {
       
     my ($self, $cgi) = @_;
        lock($signal);
        if ($signal eq 0){
                $signal = 1;
        }    
$countSignals++;
print "contador señales: $countSignals";
print "\n $timestamp receiving signal";                                

}

my $httpPublisher = threads->new(\&listenToSignal, @http_params);
my $httpConsumer = threads->new(\&updateCube, @partition_params);

my @returned_data_http = $httpPublisher->join;
my @returned_data_update = $httpConsumer->join;

sub listenToSignal{
        while ('para siempre') {
        MyWebServer->new(8084)-> run();
}
}#end sub

sub updateCube{
        while ('para siempre') {
                lock($signal);
                if ($signal > 0){
#                       lock($signal);
                        $signal = 0;
                        $countExecutions++;
                        print "\ncontador updates: $countExecutions";
                        print "\n $timestamp starting updateCube thread";      
                        sleep 5;                                                        #code for tasks comes here
                        print "\n $timestamp finishing updateCube thread";                                     
                }#end if signal yes
         }#end while forever
}#end thead update cube
Coloreado en 0.002 segundos, usando GeSHi 1.0.8.4


Exposición del problema:
El programa trabaja perfectamente si ninguno de los threads tarda en ejecutarse. Es decir, si quitamos la instrucción "sleep" del thread "httpConsumer" trabaja de miedo, pero en cuanto este thread se toma su tiempo (sleep 5) que será lo que ocurra en la realidad el servidor peta al recibir sucesivas requests (si hacemos requests cada 5 segundos sigue funcionando bien).
He hecho un montón de combinaciones y permutaciones del orden de ejecución de las instrucciones pero no parece ser el problema...

Pregunta:
¿Por qué ocurre esto?
¿Estoy haciendo algo mal verdad?

¡Muchas gracias!
Moraita
Perlero nuevo
Perlero nuevo
 
Mensajes: 36
Registrado: 2008-10-29 10:25 @475

Notapor Moraita » 2009-03-24 11:04 @502

Sólo un dato más, en mis tests he recibido de repente un mensaje de "broken pipe" ¿ésto está relacionado con la CPU?, pero no lo puedo reproducir, de hecho en los demás tests (del mismo código) no me da ningún error, el servidor se para y punto.

Y otra cosa, el sleep acelera la parada del servidor, pero he probado a hacer sin el sleep() llamadas seguidas y cuando voy por la 47 (por decir algo) se cae otra vez el servidor... o sea, que no sé si es el sleep()...

Incluyo de nuevo el programa porque he cambiado alguna cosilla y quizá sea mejor esta versión aunque con el mismo error :(

Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
#declare packages
use threads ();
use threads::shared ();
my $signal : shared = 0;
my $countSignals = 0;
my $countExecutions = 0;

my @http_params = '';
my @partition_params = '';

package MyWebServer;
use base qw(HTTP::Server::Simple::CGI);
sub handle_request {
        my $timerequest = `date '+%d/%m/%y%H%M%S'`;    
        my ($self, $cgi) = @_;
        lock($signal);
        if ($signal eq 0){
                $signal = 1;
        }    
        print "signal server: $signal \n";
        $countSignals++;
        print "contador señales: $countSignals";
        print "\n $timerequest receiving signal";                                      

}


my $httpPublisher = threads->new(\&listenToSignal, @http_params);
my $httpConsumer = threads->new(\&updateCube, @partition_params);

my @returned_data_http = $httpPublisher->join;
my @returned_data_update = $httpConsumer->join;

sub listenToSignal{
        MyWebServer->new(8084)-> run();

}#end sub

sub updateCube{

        while ('para siempre') {
                lock($signal);
                if ($signal > 0){
                        my $timestamp = `date '+%d/%m/%y%H%M%S'`;
                        print "signal update cube: $signal \n";
                        $countExecutions++;
                        print "\ncontador updates: $countExecutions";
                        print "\n $timestamp starting updateCube thread";      
                        sleep 5;                                                        #code for tasks comes here
                        print "\n$timestamp finishing updateCube thread";      
                        $signal = 0;    
                        print "signal update cube: $signal \n";                
                }#end if signal yes
         }#end while forever
}#end thead update cube
Coloreado en 0.002 segundos, usando GeSHi 1.0.8.4


Nota: bueno, no me da ningún error que yo vea, támpoco sé dónde mirar los errores del servidor :roll:

Gracias de nuevo.
Moraita
Perlero nuevo
Perlero nuevo
 
Mensajes: 36
Registrado: 2008-10-29 10:25 @475

Notapor explorer » 2009-03-24 11:57 @539

Moraita escribiste:Por otro lado, veo que ese de nivel "intermedio". Quiero pedir disculpas de antemano por mi atrevimiento. Siendo una novata, no sé si tengo derecho a escribir fuera del "básico".

No hay limitación de ninguna clase en escribir donde se quiera. Luego es tarea de los moderadores el editar, mover o borrar los mensajes enviados.

Moraita escribiste:Aquí está el código básico que engloba dos threads trabajando en paralelo. Uno inicia un servidor que recibirá señales (requets) y el otro thread que trabajará en paralelo realizando x tareas tras haber detectado que se recibieron señales en el servidor.

¿De verdad necesitas usar hilos para resolver este problema? Yo creo que es complicarlo un poco...

Yo lo que haría sería configurar HTTP::Server::Simple para que arrancase, se quedase escuchando, y luego procesar las peticiones que lleguen.

Algo así:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
#!/usr/bin/perl
use strict;
use warnings;
use diagnostics;

#######################################################
package MiServidorWeb;
use base qw(HTTP::Server::Simple);

our $numero_conexiones = 0 ;

sub handler {
    my $self = shift;

    # Se ha recibido una petición del exterior
    $numero_conexiones++;

    print  "Conexión $numero_conexiones\n";
}

sub path {
    my $self  = shift;
    my $valor = shift;

    print "Path: $valor\n";
}

######################################################
package main;
use HTTP::Server::Simple;

# Arrancamos el servidor
my $servidor = MiServidorWeb->new();

print "Conexiones al inicio: $MiServidorWeb::numero_conexiones\n";

$servidor->run();
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4

Así, tenemos funcionando un servidor HTTP escuchando por peticiones. Si nos conectamos varias veces, vemos que lleva la cuenta de las conexiones
Código: Seleccionar todo
explorer@joaquin:~/Documents/Desarrollo> telnet localhost 8080
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
GET / HTTP/1.1

Path: /
Conexión 1
Connection closed by foreign host.
explorer@joaquin:~/Documents/Desarrollo> telnet localhost 8080
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
GET / HTTP/1.1

Path: /
Conexión 2
Connection closed by foreign host.

Solo quedaría por programar qué quieres hacer en cada una de esas conexiones.
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14477
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España

Notapor Moraita » 2009-03-25 03:04 @169

El problema es que no tengo que tratar cada conexión.
El servidor HTTP solo sirve para comunicar que hay gente interesada en entrar a la cueva; luego cogeré e independiente de si hay 1 o N personas en la cola me pondré a trabajar y realizar todas las tareas, eso sí, si no hay nadie (o sea, no hubo conexiones) no hago nada.

Con tu propuesta, que me encanta por su sencillez, no sé en qué momento pondría el código para realizar las tareas. En el handle request no puedo porque no es para cada request y en el main, una vez iniciado el servidor, se supone que ya no puedo ejecutar más código a no ser que haga hilos, ¿no?

Bueno te detallaré más.

Es que hay un infraestructura muy compleja detrás de todo esto he intento informar sólo de lo necesario para no liarla.

Resulta que este mismo proceso lo tengo resuelto y funcionando perfectamente utilizando memoria compartida. Un proceso que recibe las peticiones (tickets de entrada a la cueva) y el otro que ve que en la variable compartida ha habido movimiento y se pone a trabajar.

Pero en esta infraestructura, además de utilizar máquinas diferentes, para la entrada de personas y el interior de la cueva, el arquitecto exige elegancia y sobre todo consumo mínimo de recursos y para ello propone el servidor web...

Y bueno... esto es lo que hay detrás de mi solución escogida.

Claro que siempre puedo argumentar otras soluciones... yo de momento he decidido tirar por aquí, claro que me tiene que funcionar bien y no como me está funcionando ahora... :cry:
Moraita
Perlero nuevo
Perlero nuevo
 
Mensajes: 36
Registrado: 2008-10-29 10:25 @475

Siguiente

Volver a Intermedio

¿Quién está conectado?

Usuarios navegando por este Foro: No hay usuarios registrados visitando el Foro y 26 invitados

cron