在Perl中,我如何等待线程并行结束?

时间:2010-03-16 10:09:05

标签: multithreading perl join locking

我有一个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 个答案:

答案 0 :(得分:9)

首先,对代码本身进行一些评论。你需要确保你有:

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'

答案 1 :(得分:5)

我认为您需要将从列表中提取下一个文件的代码移动到线程本身。

因此,每个线程不会只处理一个文件,而是继续处理,直到列表为空。

这样,您还可以节省创建新线程的开销。

然后你的主线程将加入它们。

当然,这需要在列表上进行同步(以便它们不会提取相同的数据)。或者,您可以将列表拆分为两个(每个线程一个),但这可能会导致不幸的分发。

(PS:没有Perl上帝,只是一个谦卑的和尚)