2010-03-16 44 views
8

我有一個Perl腳本啓動2個線程,每個處理器一個。我需要它等待一個線程結束,如果一個線程結束了一個新線程的產生。看起來join方法阻塞了程序的其餘部分,因此,第二個線程不能結束,直到第一個線程完成的任何事情都完成了,這種方式會失敗它的目的。在Perl中,我該如何等待線程並行結束?

我試過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 

回答

5

我認爲你需要移動,從列表中插入線程拉的下一個文件的代碼他們自己。

因此,每個線程不會只處理一個文件,而是繼續處理,直到列表爲空。

這樣,您還可以節省創建新線程的開銷。

您的主線程將會加入他們兩個。

當然,這需要列表上的同步(以便它們不會拉出相同的數據)。或者,您可以將列表分成兩個(每個線程一個),但這可能會導致不幸的分配。

(PS:不Perl的神,只是一個小僧)

+0

今天你是我的上帝;)不錯的解決方案.. 還有一件事: 把鎖放在列表上我現在這樣做: if(1 == 1){lock(@file_list); $ file = shift(@file_list); } 我需要這個,如果它在運行函數前在最後解鎖。這似乎是一個很不錯的黑客,但:)更好的辦法嗎? – Pmarcoen 2010-03-16 12:16:50

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'
+0

感謝您的意見,在閱讀您的意見之前已經實施了Thilo的解決方案,所以選擇了他的答案,但我一定會使用您的建議! – Pmarcoen 2010-03-16 12:20:33

+1

雖然事先對工作集進行分區可能會導致不吉利的分配。 – Thilo 2010-03-16 12:53:17