В Perl, как я могу ждать параллельного завершения потоков? - PullRequest
8 голосов
/ 16 марта 2010

У меня есть Perl-скрипт, который запускает 2 потока, по одному для каждого процессора. Мне нужно, чтобы он дождался окончания потока, если один поток заканчивается, то создается новый. Похоже, что метод join блокирует остальную часть программы, поэтому второй поток не может завершиться, пока не будет выполнено все, что делает первый поток, что в некоторой степени противоречит его цели.

Я попробовал метод is_joinable, но, похоже, он тоже этого не делает.

Вот мой код:

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

Вывод этого:

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

Ответы [ 2 ]

9 голосов
/ 16 марта 2010

Прежде всего, несколько комментариев к самому коду. Вы должны убедиться, что у вас есть:

use strict;
use warnings;

в начале каждого сценария. Во-вторых:

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

не требуется, так как массив в скалярном контексте оценивается числом элементов в массиве. То есть:

$nofiles = @ARGV;

правильно даст вам количество файлов в @ARGV независимо от значения $[.

Наконец, сценарий можно значительно упростить, разделив список файлов перед запуском потоков:

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

Выход:

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'

В качестве альтернативы, вы можете позволить потокам переходить к следующей задаче по мере их выполнения:

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

Выход:

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'
5 голосов
/ 16 марта 2010

Я думаю, вам нужно переместить код, который вытягивает следующий файл из списка, в сами потоки.

Таким образом, каждый поток будет не только обрабатывать один файл, но и продолжать обработку, пока список не станет пустым.

Таким образом, вы также постоянно экономите на создании новых тем.

Ваш основной поток присоединится к ним обоим.

Конечно, для этого требуется синхронизация в списке (чтобы они не извлекали одни и те же данные). Кроме того, вы можете разделить список на два (по одному для каждого потока), но это может привести к неудачному распределению.

(PS: Нет, Perl God, просто смиренный монах)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...