• Publicidad

Un fork en perl-gtk2

¿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.

Un fork en perl-gtk2

Notapor morago » 2011-12-07 07:00 @333

Hola.

He creado un proceso en un programilla de perl-gtk2 y Glade con la intención de que los widgets me sigan respondiendo mientras se desarrolla el proceso.

Tengo dos botones. Uno que arranca el proceso y otro que lo detiene. Al arrancar el proceso pongo el botón que lo arranca insensible ($btn_arrancar->sensitive('0')) y el cursor con un reloj de espera.

El caso es que si detengo el proceso con el botón de detener, tanto el cursor como el estado del otro botón vuelven a la normalidad. Sin embargo, cuando el proceso se detiene por sí mismo el estado del botón continúa insensible y el cursor de reloj se mantiene. No doy con el modo de que vuelva a la normalidad tras el fin natural del proceso.

Gracias por la atención.

Este es el código:

Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. #!/usr/bin/perl
  2.  
  3. use strict;
  4. use Gtk2 'init';
  5. use Gtk2::GladeXML;
  6.  
  7. use vars qw($nproc);
  8.  
  9. my $glade_app      = Gtk2::GladeXML->new("$ENV{'HOME'}/bin/Glade/GtkCNMV/pruebas/gtkfork.xml");
  10. my $cursor_normal  = Gtk2::Gdk::Cursor->new('left_ptr');
  11. my $cursor_ocupado = Gtk2::Gdk::Cursor->new('watch');
  12.  
  13. my $win          = $glade_app->get_widget('window1');
  14. my $btn_arrancar = $glade_app->get_widget('button1');
  15. my $btn_detener  = $glade_app->get_widget('button2');
  16.  
  17. $btn_arrancar->signal_connect( 'clicked' => \&arrancar );
  18. $btn_detener->signal_connect( 'clicked' => \&detener );
  19. $win->signal_connect( 'delete_event' => sub { Gtk2->main_quit; } );
  20.  
  21. $win->show_all;
  22. Gtk2->main;
  23.  
  24. sub arrancar {
  25.     $btn_arrancar->set_sensitive('0');
  26.     $btn_arrancar->window->set_cursor($cursor_ocupado);
  27.  
  28.     my $PID = fork();
  29.     if ($PID) {
  30.         $nproc = $$;
  31.  
  32.         # Mi manera cutre de pasar el número de proceso :)
  33.         open( TMP, ">/tmp/nproc" ) or die "Error: $!\n";
  34.         print TMP $nproc;
  35.         close TMP;
  36.  
  37.         foreach ( 0 .. 3 ) {
  38.             print ">> $_\n";
  39.             sleep 1;
  40.             detener() if ( $_ >= 3 );
  41.         }
  42.     }
  43.     else { return "No hay fork: $!\n"; }
  44.  
  45.     return;
  46. }
  47.  
  48. sub detener {
  49.     $btn_arrancar->set_sensitive('1');
  50.     $btn_arrancar->window->set_cursor($cursor_normal);
  51.  
  52.     open( TMP, "</tmp/nproc" ) or die "Error: $!\n";
  53.     $nproc = $_ while (<TMP>);
  54.     close TMP;
  55.  
  56.     print "Terminando $nproc...\n";
  57.     kill TERM => $nproc;
  58.  
  59.     return;
  60. }
  61.  
Coloreado en 0.007 segundos, usando GeSHi 1.0.8.4
Adjuntos
gtkfork.xml
Adjunto el archivo glade
(3.11 KiB) 121 veces
Última edición por explorer el 2011-12-07 10:42 @487, editado 1 vez en total
Razón: Formateado de código con Perltidy
Avatar de Usuario
morago
Perlero nuevo
Perlero nuevo
 
Mensajes: 10
Registrado: 2009-12-13 16:43 @738

Publicidad

Re: Un fork en perl-gtk2

Notapor explorer » 2011-12-07 12:29 @561

No lo he mirado mucho, pero veo algunas cosas...

En la línea 28 se hace el fork(). En ese momento se crea un proceso hijo que es idéntico al padre, y los dos siguen en ese mismo punto.

En la línea 29 se pregunta por el valor de $PID.

Si $PID está definido y es positivo, resulta que somos el padre ($PID es el número de proceso del hijo).

Entonces, el padre entra en el bucle de espera de 3 segundos, al final de los cuales, se detiene.

En cambio, el hijo al llegar a la línea 29, recibe un valor 0 (el $PID dentro del hijo vale 0), con lo que salta directamente a la línea 43, donde hace un return y termina el proceso asociado a la pulsación del botón, y se queda esperando. (No es cierto lo que pones en la línea 43, de que no se ha generado el fork(). Para saber si el fork() ha funcionado bien o no, debes comprobar si el valor devuelto por fork() está definido o no (undef).)

Más información en perldoc -f fork .
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14480
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España

Re: Un fork en perl-gtk2

Notapor salva » 2011-12-08 10:29 @478

El problema es que tanto el padre como el hijo están accediendo a la interfaz gráfica y eso no funciona.

La forma correcta de enfocar este tipo de problemas es hacer que un solo proceso (normalmente el padre) se encargue en exclusiva de controlar la interfaz gráfica a la vez que habla con el hijo (habitualmente mediante pipes) que hará otro tipo de tareas (algún cálculo, acceder a algún servicio de red, etc).

Una vez que el hijo termina, tiene que llamar a POSIX::_exit() para salir sin llamar a los métodos DESTROY ni a los bloques END, lo cual también interferiría con el GUI.
Avatar de Usuario
salva
Perlero nuevo
Perlero nuevo
 
Mensajes: 200
Registrado: 2008-01-03 15:19 @680

Re: Un fork en perl-gtk2

Notapor morago » 2011-12-11 13:07 @588

Gracias por vuestras respuestas. He encontrado un par de scripts que muestran el uso de threads en Gtk2:
http://code.google.com/p/saaral-soft-search-spider/
http://cpansearch.perl.org/src/FLORA/Gtk2-1.223/examples/thread_usage.pl

El autor del primero dice que Gtk2::GladeXML no se lleva muy bien con los hilos.
Avatar de Usuario
morago
Perlero nuevo
Perlero nuevo
 
Mensajes: 10
Registrado: 2009-12-13 16:43 @738

Re: Un fork en perl-gtk2

Notapor morago » 2011-12-13 19:17 @845

Parece que el uso de threads supone una solución muy simple y limpia. Aunque aún no lo he probado con glade, este es el ejemplo que me ha quedado:

Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. #!/usr/bin/env perl
  2.  
  3. use strict;
  4. use warnings;
  5. use threads;
  6. use LWP::Simple;
  7. use Glib qw(TRUE FALSE);
  8. use Gtk2 qw/-init -threads-init/;
  9.  
  10.  
  11. my $hebra_down;
  12. my $deb = ( 'firefox_8.0+build1-0ubuntu0.11.10.3_amd64.deb' );
  13. my $firefox = ( "http://security.ubuntu.com/ubuntu/pool/main/f/firefox/$deb" );
  14.  
  15. die "Glib::Object thread safetly failed"
  16.         unless Glib::Object->set_threadsafe (TRUE);
  17.  
  18. my $win = Gtk2::Window->new;
  19. $win->signal_connect (destroy => sub { Gtk2->main_quit; });
  20. $win->set_title ($0);
  21. $win->set_border_width (10);
  22. $win->set_default_size (400, 200);
  23. $win->set_position('center');
  24.  
  25. my $hbox = Gtk2::HBox->new (FALSE, 6);
  26. $win->add ($hbox);
  27.  
  28. my $vbox = Gtk2::VBox->new (FALSE, 6);
  29. $hbox->pack_start ($vbox, FALSE, FALSE, 0);
  30.  
  31. my $btn_iniciar = Gtk2::Button->new ('Iniciar');
  32. $vbox->pack_start ($btn_iniciar, FALSE, FALSE, 0);
  33.  
  34. my $btn_parar = Gtk2::Button->new ('Parar');
  35. $vbox->pack_start ($btn_parar, FALSE, FALSE, 0);
  36. $btn_parar->set_sensitive(FALSE);
  37.  
  38. my $pending = Gtk2::Label->new ('Pulsar para iniciar la descarga');
  39. $vbox->pack_start ($pending, FALSE, FALSE, 0);
  40.  
  41. $btn_iniciar->signal_connect (clicked => sub {
  42.                 local $SIG{KILL} = sub { threads->exit };
  43.  
  44.                 $btn_iniciar->set_sensitive (FALSE);
  45.                 $btn_parar->set_sensitive(TRUE);
  46.                 $pending->set_label('Descargando');
  47.                
  48.                 $hebra_down = threads->create(\&procesa_descarga);
  49.                 });
  50.  
  51. $btn_parar->signal_connect (clicked => sub {
  52.                 $hebra_down->kill('KILL')->detach;
  53.                
  54.                 $btn_iniciar->set_sensitive (TRUE);
  55.                 $btn_parar->set_sensitive(FALSE);
  56.                 $pending->set_label('Descarga cancelada');
  57.                 });
  58.  
  59. $win->show_all;
  60. Gtk2->main;
  61.  
  62.  
  63. sub procesa_descarga
  64.   {
  65.         my $ua = LWP::UserAgent->new();
  66.         $ua->timeout(10);
  67.         my $response = $ua->get( $firefox );
  68.  
  69.         if ($response->is_success)
  70.           {
  71.           open (FF, ">$deb") or return "Error volcando a disco: $!\n";
  72.           print FF $response->decoded_content();
  73.           close FF;
  74.  
  75.           $btn_iniciar->set_sensitive(TRUE);
  76.           $btn_parar->set_sensitive(FALSE);
  77.  
  78.           if ($response->status_line eq '200 OK')
  79.             {
  80.             $pending->set_label("Descarga completa\n" . $response->status_line);
  81.             return $response->status_line;
  82.             }
  83.           else
  84.             {
  85.             $pending->set_label("Se ha producido un error\n" . $response->status_line);
  86.             return $response->status_line;
  87.             }
  88.           }
  89.         else {
  90.             $pending->set_label("Se ha producido un error\n" . $response->status_line);
  91.             return $response->status_line;
  92.             }
  93.   }
  94.  
Coloreado en 0.003 segundos, usando GeSHi 1.0.8.4
Última edición por morago el 2011-12-14 05:41 @279, editado 1 vez en total
Avatar de Usuario
morago
Perlero nuevo
Perlero nuevo
 
Mensajes: 10
Registrado: 2009-12-13 16:43 @738


Volver a Intermedio

¿Quién está conectado?

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