2013-05-09 113 views
0

看来在线程中使用管道可能会导致线程变成僵尸。事实上,管道中的命令被转化为僵尸,而不是线程。这不会发生很烦人的时间,因为很难找出真正的问题。如何处理这个问题?是什么导致这些?它与管道有关吗?如何避免这种情况?如何处理perl中变成僵尸的多线程

以下是创建示例文件的代码。

#buildTest.pl 
use strict; 
use warnings; 

sub generateChrs{ 
    my ($outfile, $num, $range)[email protected]_; 
    open OUTPUT, "|gzip>$outfile"; 
    my @set=('A','T','C','G'); 
    my $cnt=0; 
    while ($cnt<$num) { 
     # body... 
     my $pos=int(rand($range)); 
     my $str = join '' => map $set[rand @set], 1 .. rand(200)+1; 
     print OUTPUT "$cnt\t$pos\t$str\n"; 
     $cnt++ 
    } 
    close OUTPUT; 
} 

sub new_chr{ 
    my @chrs=1..22; 
    push @chrs,("X","Y","M", "Other"); 
    return @chrs; 
} 

for my $chr (&new_chr){ 
    generateChrs("$chr.gz",50000,100000) 
} 

以下代码会偶尔创建僵尸线程。原因或触发因素仍然未知。

#paralRM.pl 
use strict; 
use threads; 
use Thread::Semaphore; 
my $s = Thread::Semaphore->new(10); 

sub rmDup{ 
    my $reads_chr=$_[0]; 
    print "remove duplication $reads_chr START TIME: ",`date`; 
    return 0 if(!-s $reads_chr); 

    my $dup_removed_file=$reads_chr . ".rm.gz"; 
    $s->down(); 
    open READCHR, "gunzip -c $reads_chr |sort -n -k2 |" or die "Error: cannot open $reads_chr"; 
    open OUTPUT, "|sort -k4 -n|gzip>$dup_removed_file"; 

    my ($last_id, $last_pos, $last_reads)=split('\t',<READCHR>); 
    chomp($last_reads); 
    my $last_length=length($last_reads); 
    my $removalCnts=0; 

    while (<READCHR>) { 
     chomp; 
     my @line=split('\t',$_); 
     my ($id, $pos, $reads)[email protected]; 
     my $cur_length=length($reads); 
     if($last_pos==$pos){ 
      #may dup 
      if($cur_length>$last_length){ 
       ($last_id, $last_pos, $last_reads)[email protected]; 
       $last_length=$cur_length; 
      } 
      $removalCnts++; 
      next; 
     }else{ 
      #not dup 
     } 
     print OUTPUT join("\t",$last_id, $last_pos, $last_reads, $last_length, "\n"); 
     ($last_id, $last_pos, $last_reads)[email protected]; 
     $last_length=$cur_length; 
    } 

    print OUTPUT join("\t",$last_id, $last_pos, $last_reads, $last_length, "\n"); 
    close OUTPUT; 
    close READCHR; 
    $s->up(); 
    print "remove duplication $reads_chr END TIME: ",`date`; 
    #unlink("$reads_chr") 
    return $removalCnts; 
} 


sub parallelRMdup{ 
    my @[email protected]_; 
    my %jobs; 
    my @removedCnts; 
    my @processing; 

    foreach my $chr(@chrs){ 
     while (${$s}<=0) { 
      # body... 
      sleep 10; 
     } 
     $jobs{$chr}=async { 
      return &rmDup("$chr.gz") 
      } 
     push @processing, $chr; 
    }; 

    #wait for all threads finish 
    foreach my $chr(@processing){ 
     push @removedCnts, $jobs{$chr}->join(); 
    } 
} 

sub new_chr{ 
    my @chrs=1..22; 
    push @chrs,("X","Y","M", "Other"); 
    return @chrs; 
} 

&parallelRMdup(&new_chr); 
+1

是否所有的线程都报告了合理的开始和结束时间?但是我看不到任何明显错误的代码,可能导致线程无法连接。但是,有一些不好的做法:①你在'async'块之后错过了一个分号吗? ②产卵时不要忙于等待。并且不要取消引用Semaphore对象。相反,你可以在发出信号之前“下”信号量,但是在线程结束时“上升”会好得多。 ③您应该以编程方式声明所有'@ chrs'都是唯一的,否则您将只加入'$ chr'的最后一个线程。 – amon 2013-05-09 07:43:52

+0

僵尸是在管道中创建的(排序,gzip等)。谢谢你的建议。我学到了很多! – Gahoo 2013-05-09 10:53:58

回答

0

由于您对原始帖子的评论建议 - 您的代码在这里没有任何明显的错误。可能有助于理解的是一个zombie过程。

具体来说 - 这是一个催生的过程(由您的open)已退出,但父母尚未收集它的返回码。

对于较短的运行代码,这并不是那么重要 - 当你的主程序退出时,僵尸将“重新启动”为init,这将自动清除它们。

对于长时间运行,您可以使用waitpid进行清理并收集返回代码。

现在在这个特定的情况下 - 我看不到具体的问题,但我会猜想这与你如何打开你的文件句柄有关。像你这样打开文件句柄的缺点是,它们在全局范围内 - 当你做事情时,这只是一个坏消息。

,如果你改变了你的open调用我会想象:

my $pid = open (my $exec_fh, "|-", "executable"); 

,然后在该$pid下你的close那么你的僵尸会完成所谓的waitpid。测试从waitpid获得的回报,以了解您的哪位高管出错(如果有),这应该可以帮助您找出原因。

或者 - 设置$SIG{CHLD} = "IGNORE";这意味着你 - 有效地告诉你的子进程'立即消失' - 但是如果它们死了,你将无法从它们那里得到返回代码。