【问题标题】:How to stop forking in this code如何在此代码中停止分叉
【发布时间】:2017-03-18 22:53:03
【问题描述】:

所以我有一个 perl 脚本可以输出并获取流的片段(我不知道有多少片段)

但我想不出一个知道何时停止工作的好方法。现在如果 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`;

【问题讨论】:

    标签: perl fork


    【解决方案1】:

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

    上网时更是如此,因为您可能会惹恼服务器(而且很快就会停止运行速度)。一次可能运行多达几十个进程?

    然后一个选项是使用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 为例传递实际数据。

    有关更多信息,请参阅文档,但这可以满足您的要求。更多Forks::Super


    如果你想像你一样分叉,我会先在那里放一点sleep,这样你就不会用太多的请求轰炸服务器。您的孩子可以使用socketpair 与家长交谈。失败的孩子可以写,而其他人可以简单地关闭他们的套接字。父级不断检查,例如使用来自IO::Selectcan_readperlipc 中有一个例子。由于您只需要孩子给父母写信,pipe 就足够了。

    你也可以用信号来做。失败的子节点发送(比如说)SIGUSR1 给父节点,父节点捕获并设置一个全局变量来控制进一步的分叉。这更简单,因为父级只捕获一个信号,而不关心它来自哪里。请参阅 perlipcsigtrap 编译指示。

    您也可以像您一样使用文件,这可能是最简单的,因为在这里您不关心赛车问题(孩子是否写重叠),而只关心显示的空文件。

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

    最后,还有一些模块可以帮助处理外部命令,例如IPC::Run


    要在每个孩子退出时正确运行回调,请使用reap_finished_children。见this post

    【讨论】:

    • 感谢您提供的信息,我会做一些研究。我不知道一个特定的流有多少文件,所以现在我去直到 wget 失败。但你说得对,我应该考虑服务器而不是轰炸它。我运行的最后一个是超过 357 段视频。不过它确实下载得很快。
    • @genx1mx6 我希望它有所帮助,即使它主要列出了方法。我计划用P::FM 添加代码(现在已经很晚了)。父级简单地运行循环,因此立即创建了那些 357,您可能会立即使用所有这些来访问服务器。肯定还有更多,因为父母没有时间回到下一次迭代中再分叉,所以它只是继续旋转它们。即使一个失败,也可能有一大群人已经在尝试下载其他不存在的文件。顺便说一句,你使用的文件很好——但你需要限制数量。
    • @genx1mx6 我用Parallel::ForkManager 添加了一个工作示例。我也可以为其他方法添加代码,但请告诉我这是怎么回事。
    猜你喜欢
    • 2015-03-02
    • 2022-01-12
    • 1970-01-01
    • 2016-01-02
    • 2016-08-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多