【问题标题】:In Perl, how can I wait for threads to end in parallel?在 Perl 中,如何等待线程并行结束?
【发布时间】:2010-03-16 10:09:04
【问题描述】:

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

【问题讨论】:

    标签: multithreading perl join locking


    【解决方案1】:

    首先,关于代码本身的一些 cmets。你需要确保你有:

    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
    用 1 2 3 调用
    打开“1”
    用 4 5 跟注
    打开'4'
    关闭“4”
    打开'5'
    关闭“1”
    打开'2'
    关闭“5”
    关闭“2”
    打开'3'
    关闭'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
    线程 1 盯着
    1:打开“1”
    1:关闭“1”
    1:打开“2”
    线程 2 盯着
    2:打开“3”
    2:关闭“3”
    2:打开“4”
    1:关闭“2”
    1:开“5”
    1:关闭“5”
    2:关闭'4'

    【讨论】:

    • 感谢您的 cmets,在阅读您的 cmets 之前已经实施了 Thilo 的解决方案,因此选择了他的答案,但我一定会使用您的建议!
    • 事先对工作集进行分区可能会导致不吉利的分布。
    【解决方案2】:

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

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

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

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

    当然,这需要对列表进行同步(这样它们就不会拉取相同的数据)。或者,您可以将列表一分为二(每个线程一个),但这可能会导致分发不顺利。

    (PS:没有Perl大神,只是个卑微的和尚)

    【讨论】:

    • 今天你是我的上帝 ;) 不错的解决方案.. 还有一件事:要将锁放在列表中,我现在这样做:if(1==1){ lock(@file_list); $file = shift(@file_list);我需要这个 if 以便在运行函数之前最后解锁。不过,这似乎是一个相当菜鸟的黑客:) 有更好的方法吗?
    猜你喜欢
    • 2012-07-13
    • 2021-11-03
    • 2013-02-03
    • 1970-01-01
    • 1970-01-01
    • 2022-12-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多