2010-03-16 11 views
8

Ho uno script Perl che avvia 2 thread, uno per ciascun processore. Ho bisogno di aspettare che un thread finisca, se un thread termina ne viene generato uno nuovo. Sembra che il metodo di join blocchi il resto del programma, quindi il secondo thread non può finire fino a quando non viene eseguito tutto il primo thread che sconfigge il suo scopo.In Perl, come posso aspettare che i thread terminino in parallelo?

Ho provato il metodo is_joinable ma non sembra neanche farlo.

Ecco alcuni del mio codice:

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"; 
} 

L'output di questo è:

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 

risposta

5

penso che è necessario spostare il codice che tira il file successivo nella lista nelle discussioni loro stessi.

Quindi ogni thread non solo elaborerebbe un file, ma continuerà a elaborare fino a quando l'elenco non è vuoto.

In questo modo, si risparmia anche sul sovraccarico di creare nuovi thread in ogni momento.

Il tuo thread principale si unirà a entrambi.

Ovviamente, ciò richiede la sincronizzazione nell'elenco (in modo che non richiamino gli stessi dati). In alternativa, è possibile dividere l'elenco in due (uno per ogni thread), ma ciò potrebbe comportare una distribuzione sfortunata.

(PS: No Perl Dio, solo un umile frate)

+0

Oggi sei il mio dio;) bella soluzione .. Un'altra cosa: Per mettere i blocchi sulla lista Ora faccio questo: if (1 == 1) {lock (@file_list); $ file = shift (@file_list); } Ho bisogno di questo se è stato sbloccato alla fine prima di eseguire la funzione. Questo sembra un bel noob-hack però :) in qualche modo migliore? – Pmarcoen

9

Prima di tutto, un paio di osservazioni sul codice stesso. È necessario assicurarsi di avere:

use strict; 
use warnings; 

all'inizio di ogni script. Secondo:

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

non è necessario poiché una matrice in contesto scalare valuta il numero di elementi nella matrice. Cioè:

$nofiles = @ARGV; 

sarebbe correttamente si darà il numero di file in @ARGV indipendentemente dal valore di $[.

Infine, lo script può essere reso molto più semplice suddividendo la lista dei file prima di iniziare le discussioni:

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"; 
    } 
} 

uscita:

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'

In alternativa, è possibile lasciare che i fili passano alla l'attività successiva come sono:

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"; 
    } 
} 

Uscita:

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

grazie per i vostri commenti, già implementata la soluzione di Thilo prima di leggere i vostri commenti quindi ho scelto la sua risposta ma userò sicuramente i vostri suggerimenti! – Pmarcoen

+1

Partizionare il set di lavoro in anticipo potrebbe portare a una distribuzione sfortunata, però. – Thilo

Problemi correlati