如何在此代码中停止分叉

时间:2016-11-05 01:38:03

标签: perl fork

所以我有一个perl脚本,可以在那里找到一些流(我不知道有多少件是预先存在的)

但我无法想出一个知道什么时候停止wget&#f 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`;

1 个答案:

答案 0 :(得分:3)

您没有指出可能有多少进程,但没有资源是无限制的,您应该限制数量,否则当达到饱和时您会看到性能快速下降。

在外出网络时更是如此,因为你可能会烦恼服务器(事情也会很快停止加速)。也许一次可以运行几十个进程?

然后一个选项是使用Parallel::ForkManager限制多个并行下载。它有return data to parent的方式,所以孩子可以报告失败。然后它的run_on_finish方法可以检查每个批次是否有这样的标志(失败),并设置一个控制分叉的变量。

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

my $pm = Parallel::ForkManager->new(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::Selectcan_readperlipc中有一个例子。由于您只需要孩子写信给父母,pipe就足够了。

您也可以使用信号进行操作。失败的孩子将(例如)SIGUSR1发送给父母,父母将其捕获并设置一个控制更多分叉的全局变量。这更简单,因为父母只捕获一个信号,而不关心它来自何处。请参阅perlipcsigtrap pragma。

你也可以使用一个文件,就像你一样,这可能是最简单的,因为在这里你不关心赛车问题(儿童是否写重叠),而只关注一个空文件出现。

但是,在所有这些中,您还希望限制并行进程的数量。

最后,有许多模块可以帮助解决这个问题,例如IPC::Run

* 要在每个子项退出时使用reap_finished_children运行回调权限。请参阅this post