2010-03-16 10 views
8

각 프로세서 당 하나씩 2 개의 스레드를 시작하는 Perl 스크립트가 있습니다. 스레드가 종료 될 때까지 기다려야합니다. 한 스레드가 종료되면 새 스레드가 생성됩니다. 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 : 없음 펄 하나님, 단지 겸손 스님)

+0

오늘 너는 내 신이다.) 멋진 해결책 .. 한 가지 더 : 목록에 잠금을 설정하려면 다음을 수행하십시오. if (1 == 1) {lock (@file_list); $ file = shift (@file_list); } 함수를 실행하기 전에 끝에 잠금이 해제되도록하려면이 값이 필요합니다. 이것은 꽤 멍청한 - 해킹처럼 보이지만 :) 더 좋은 방법이 있습니까? – Pmarcoen

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

+1

미리 작업 세트를 분할하면 불운 한 배포로 이어질 수 있습니다. – Thilo

관련 문제