2016-11-05 49 views
1

因此,我有一個perl腳本,它出去了,並且有一個流的片斷(我不知道有多少片有預付)如何在此代碼中停止分叉

但我想不出知道何時停止wget'ing的好方法。現在如果wget返回失敗,我們創建一個名爲「end」的文件,一旦主程序看到它,它就會停止循環。有沒有更好的方式去做這件事?

很明顯,如果順序完成而不是同時完成,很簡單,但我試圖使其下載速度最快。

my $link = $ARGV[0]; 
my ($url) = $link=~ m/(.+-)\d+.ts/i; 

my $num = 0; 

#while the file END doesn't exist 
my @pids; 
while (! -e "END") { 
     #create the URL, increment by 1 
     my $video=$url.++$num.".ts"; 
     die "could not fork" unless defined (my $pid = fork()); 

     #child process goes until wget returns invalid, create END 
     if (not $pid) { 
       system ("wget -T 5 -t 5 $video"); 
       `touch END` if $? != 0; 
       exit; 
     } 
     push @pids, $pid; 
} 

#parent process still running, waiting for the same END file. 
for my $pid (@pids) { waitpid $pid,0; } 

print "pids finished\n"; 

sleep 1; 
`rm END`; 

回答

3

你沒有說明有多少過程可能會有,但沒有資源是無限的,你應該限制數量,否則你會看到的性能迅速下降爲您達到飽和。

在網絡上外出時更是如此,因爲您可能會煩惱服務器(而且事情也會很快停止)。也許一次只能運行幾十個進程?

然後一個選項是使用Parallel::ForkManager限制多個並行下載。它有一個方法return data to parent,所以一個孩子可以報告失敗。然後它的run_on_finish方法可以檢查每個批次的這種標誌(失敗),並設置一個控制分叉的變量。

use warnings; 
use strict; 
use Parallel::ForkManager;  

my $pm = new Parallel::ForkManager(2); # only 2 for a managable demo 
my $stop_forking; 

# The sub gets 6 parameters, but only first (pid) is always defined 
# The last one is what a child process may have passed 
$pm->run_on_finish( 
    sub { $stop_forking = 1 if defined $_[-1] } 
); 

for my $i (0..9) 
{ 
    last if $stop_forking; 

    $pm->start and next; # forks 
    my $ret = run_job($i); # child process 

    # Pass data to parent under a condition 
    if ($ret eq 'FAIL') { $pm->finish(0, \$ret) } # child exits 
    else    { $pm->finish } 
} 
$pm->wait_all_children; 

sub run_job { 
    my ($i) = $_[0]; 
    sleep 2; 
    print "Child: job $i exiting\n"; 
    return ($i == 3 ? 'FAIL' : 1); 
} 

在一批作業中停止分揀後,其中$i == 3。添加打印件進行診斷。

評論。此評論有用嗎? 「回調」run_on_finish僅在整批完成後才運行。 ∗匿名子其中總是收到6個參數,但只有第一個,子pid,總是被定義。最後一個數據可能是由孩子傳遞的,當發生這種情況時,我們設置標誌。一個孩子可以通過將標量引用傳遞給finish方法來返回數據。爲了表示一個條件,我們可以簡單地通過任何事情我使用\$ret作爲傳遞實際數據的示例。

查看更多文檔,但這是你需要的。


如果你想叉子你做什麼,我會先放少許sleep存在,所以你不要轟炸太多請求的服務器。您的孩子可以使用socketpair與父母交談。失敗的孩子可以寫,而所有其他人可以簡單地關閉他們的插座。家長不斷檢查,例如從IO::Select。在perlipc中有一個例子。由於您只需要孩子寫信給父母,pipe就足夠了。

您也可以用信號做到這一點。失敗的孩子向父母發送(說)SIGUSR1,父母會捕獲並設置一個控制更多分叉的全局變量。這更簡單,因爲父母只捕獲一個信號而不關心它來自哪裏。請參閱perlipcsigtrap編譯指示。

你也可以使用一個文件,就像你一樣,這可能是最簡單的,因爲在這裏你不關心賽車問題(不管兒童是否重疊),而只是一個空文件出現。

但是,在所有這些你也想限制並行進程的數量。

最後,有很多模塊可以幫助解決這個問題,例如IPC::Run


&highast;當每個孩子退出時使用回撥權reap_finished_children。請參閱this post

+0

感謝您的信息,我會做一些研究。我不知道有多少文件到一個特定的流,所以現在我去,直到wget失敗。但你說得對,我應該考慮服務器而不是轟炸它。我跑過去的最後一個是超過357件視頻。它確實下載真的很快。 – genx1mx6

+0

@ genx1mx6我希望它可以幫助,即使它主要列出方法。我打算用'P :: FM'添加代碼(現在已經晚了)。父母只是簡單地通過循環,以便357被立即創建,並且您可能會立即觸及服務器。必須有更多,因爲它需要父母沒有時間在下一個迭代中再次分叉,所以它只是繼續旋轉它們。即使一個人失敗,也可能有一大堆人試圖下載其他不存在的文件。順便說一句,你使用的文件是好的 - 但你需要限制的數量。 – zdim

+0

@ genx1mx6我用'Parallel :: ForkManager'添加了一個工作示例。我也可以爲其他方法添加代碼,但請告訴我這是怎麼回事。 – zdim