2010-03-16 16 views
8

Tengo un script Perl que inicia 2 hilos, uno para cada procesador. Necesito que espere a que termine un hilo, si un hilo termina uno nuevo se genera. Parece que el método de unión bloquea el resto del programa, por lo tanto, el segundo subproceso no puede finalizar hasta que todo lo que hace el primer subproceso termine, lo que de alguna manera frustra su propósito.En Perl, ¿cómo puedo esperar a que los hilos terminen en paralelo?

Probé el método is_joinable pero eso tampoco parece hacerlo.

Aquí es parte de mi código:

use threads; 
use threads::shared; 

@file_list = @ARGV;  #Our file list 
$nofiles = $#file_list + 1; #Real number of files 
$currfile = 1;  #Current number of file to process 

my %MSG : shared;    #shared hash 

$thr0 = threads->new(\&process, shift(@file_list)); 
$currfile++; 
$thr1 = threads->new(\&process, shift(@file_list)); 
$currfile++; 

while(1){ 
if ($thr0->is_joinable()) { 
    $thr0->join; 
     #check if there are files left to process 
    if($currfile <= $nofiles){ 
    $thr0 = threads->new(\&process, shift(@file_list)); 
    $currfile++; 
    } 
} 

if ($thr1->is_joinable()) { 
    $thr1->join; 
     #check if there are files left to process 
    if($currfile <= $nofiles){ 
    $thr1 = threads->new(\&process, shift(@file_list)); 
    $currfile++; 
    } 
} 
} 

sub process{ 
     print "Opening $currfile of $nofiles\n"; 
     #do some stuff 
     if(some condition){ 
       lock(%MSG); 
       #write stuff to hash 
     } 
     print "Closing $currfile of $nofiles\n"; 
} 

La salida de este es:

Opening 1 of 4 
Opening 2 of 4 
Closing 1 of 4 
Opening 3 of 4 
Closing 3 of 4 
Opening 4 of 4 
Closing 2 of 4 
Closing 4 of 4 

Respuesta

5

Creo que se necesita para mover el código que arrastra el archivo siguiente de la lista en las roscas sí mismos.

De modo que cada hilo no solo procesaría un archivo, sino que continuaría procesándolo hasta que la lista esté vacía.

De esta manera, también ahorra en la sobrecarga de la creación de nuevos hilos todo el tiempo.

Su hilo principal unirá las dos.

Por supuesto, esto requiere sincronización en la lista (para que no extraigan los mismos datos). Alternativamente, podría dividir la lista en dos (una para cada hilo), pero eso podría dar como resultado una distribución desafortunada.

(PD: No se Perl Dios, sólo un humilde monje)

+0

Hoy eres mi dios;) buena solución .. Una cosa más: Para poner los bloqueos en la lista ahora hago esto: if (1 == 1) {lock (@file_list); $ file = shift (@file_list); } Necesito esto si es así para que se desbloquee al final antes de ejecutar la función. Esto parece un bonito noob-hack :) ¿alguna forma mejor? – Pmarcoen

9

En primer lugar, algunos comentarios sobre el propio código. Usted necesita asegurarse de que tiene:

use strict; 
use warnings; 

al comienzo de cada guión. Segundo:

@file_list = @ARGV;  #Our file list 
$nofiles = $#file_list + 1; #Real number of files 

es innecesario ya que una matriz en el contexto escalar evalúa la cantidad de elementos en la matriz. Es decir:

$nofiles = @ARGV; 

habría correctamente le dará el número de archivos en @ARGV independientemente del valor de $[.

Por último, la secuencia de comandos se puede hacer mucho más simple mediante la partición de la lista de archivos antes de iniciar las discusiones:

use strict; use warnings; 

use threads; 
use threads::shared; 

my @threads = (
    threads->new(\&process, @ARGV[0 .. @ARGV/2]), 
    threads->new(\&process, @ARGV[@ARGV/2 + 1 .. @ARGV - 1]), 
); 

$_->join for @threads; 

sub process { 
    my @files = @_; 
    warn "called with @files\n"; 
    for my $file (@files) { 
     warn "opening '$file'\n"; 
     sleep rand 3; 
     warn "closing '$file'\n"; 
    } 
} 

Salida:

C:\Temp> thr 1 2 3 4 5 
called with 1 2 3 
opening '1' 
called with 4 5 
opening '4' 
closing '4' 
opening '5' 
closing '1' 
opening '2' 
closing '5' 
closing '2' 
opening '3' 
closing '3'

Alternativamente, puede dejar que los hilos pasan a la siguiente tarea como están hechas:

use strict; use warnings; 

use threads; 
use threads::shared; 

my $current :shared; 
$current = 0; 

my @threads = map { threads->new(\&process, $_) } 1 .. 2; 
$_->join for @threads; 

sub process { 
    my ($thr) = @_; 
    warn "thread $thr stared\n"; 

    while (1) { 
     my $file; 
     { 
      lock $current; 
      return unless $current < @ARGV; 
      $file = $ARGV[$current]; 
      ++ $current; 
     } 
     warn "$thr: opening '$file'\n"; 
     sleep rand 5; 
     warn "$thr: closing '$file'\n"; 
    } 
} 

Salida:

C:\Temp> thr 1 2 3 4 5 
thread 1 stared 
1: opening '1' 
1: closing '1' 
1: opening '2' 
thread 2 stared 
2: opening '3' 
2: closing '3' 
2: opening '4' 
1: closing '2' 
1: opening '5' 
1: closing '5' 
2: closing '4'
+0

gracias por sus comentarios, ya implementé la solución de Thilo antes de leer sus comentarios, así que elegí su respuesta, ¡pero sin duda utilizaré sus sugerencias! – Pmarcoen

+1

Sin embargo, dividir el conjunto de trabajo de antemano podría conducir a una distribución desafortunada. – Thilo

Cuestiones relacionadas