Perl,Parallel::ForkManager - 如何实现 fork 超时

Posted

技术标签:

【中文标题】Perl,Parallel::ForkManager - 如何实现 fork 超时【英文标题】:Perl, Parallel::ForkManager - how to implement timeout for fork 【发布时间】:2012-06-10 18:04:27 【问题描述】:

是否可以使用 Parallel::ForkManager 为 fork 实现某种超时(时间限制)?

基本的 Parallel::ForkManager 脚本如下所示

use Parallel::ForkManager;
my $pm = Parallel::ForkManager->new( 10 );
for ( 1 .. 1000 ) 
    $pm->start and next;
    # some job for fork
    $pm->finish;

$pm->wait_all_children();

我想限制“# some job for fork”的时间。例如,如果它没有在 90 秒内完成。那么它(叉子)应该被杀死/终止。 我想到了using this,但我不得不说,我不知道如何将它与 Parallel::ForkManager 一起使用。

编辑

感谢 hobbs 和 ikegami。您的两个建议都有效.....但仅在这个基本示例中,而不是在我的实际脚本中:(。 这些叉子将永远存在,而且 - 老实说 - 我不知道为什么。我使用这个脚本几个月。没有改变任何东西(尽管很多事情取决于外部变量)。 每个分叉都必须从网站下载页面,对其进行解析并将结果保存到文件中。每个分叉的时间不应超过 30 秒。超时设置为 180 秒。那些吊叉是完全随机的,因此很难追踪问题。这就是为什么我想出了一个临时的、简单的解决方案 - 超时和终止。

什么可能会禁用(中断)我的代码中的超时方法?我的代码中没有任何其他alarm()

编辑 2

其中一个叉子挂了 1 小时 38 分钟并返回“超时 PID”——这是我在 die() 中输入的 alarm()。所以超时工作......但它迟到了大约 1h36,5m ;)。你有什么想法吗?

【问题讨论】:

Re: 编辑 2,你在使用 LWP::UA 吗?如果是这样,请参阅此处:***.com/questions/73308 “这是我在 die() 中为 alarm() 输入的内容”是什么意思? @pilcrow 我正在使用 LWP::UA(通过 WWW::Mechanize)。早些时候,当我跟踪这个问题时,我在 WWW::Mech http 请求上测试了“超时”。超时有效,但叉子还是挂了。 @ikegami 我在eval 之后有if ($@) die "timeout $$\n"; (from this example。对不起我的英语,它很糟糕,有时我很难解释一些事情;)。 嗯?这与警报无关。但这并不重要。你刚才说你使用LWP,它使用alarm,消除你的警报。 【参考方案1】:

更新

很抱歉在收盘后更新,但如果我没有指出 Parallel::ForkManager 也支持 run_on_start 回调,那我就失职了。这可用于安装“子注册”功能,为您处理 PID 的time()-stamping。

例如,

$pm->run_on_start(sub  my $pid = shift; $workers$pid = time(); );

结果是,结合run_on_wait 如下所述,P::FM 的主循环不需要做任何特别的事情。也就是说,它可以保持一个简单的$pm->start and next,而回调将处理其他所有事情。

原答案

Parallel::ForkManager 的 run_on_wait 处理程序和一些记账功能可以强制挂起和防 ALRM 的子级终止。

该函数注册的回调可以定期运行,而$pm 等待子终止。

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

use constant PATIENCE => 90; # seconds

our %workers;

sub dismiss_hung_workers 
  while (my ($pid, $started_at) = each %workers) 
    next unless time() - $started_at > PATIENCE;
    kill TERM => $pid;
    delete $workers$pid;
  


...

sub main 
  my $pm = Parallel::ForkManager->new(10);
  $pm->run_on_wait(\&dismiss_hung_workers, 1);  # 1 second between callback invocations

  for (1 .. 1000) 
    if (my $pid = $pm->start) 
      $workers$pid = time();
      next;
    
    # Here we are child.  Do some work.
    # (Maybe install a $SIGTERM handler for graceful shutdown!)
    ...
    $pm->finish;
  

  $pm->wait_all_children;


(正如其他人建议的那样,最好让孩子们通过alarm() 进行自我调节,但这对您来说似乎间歇性地行不通。您还可以诉诸浪费的、粗俗的技巧,例如让每个孩子自己fork() or exec('bash', '-c', 'sleep 90; kill -TERM $PPID')。)

【讨论】:

谢谢!!!它的工作:)。虽然我仍然很好奇为什么alarm() 解决方案不起作用。也许回答这个问题可以帮助我追踪“挂代码”。 谢谢。上面的这段代码也对我有用。但脚本完成后,我发现 ssh 进程仍然挂起。我不知道它们会保留多久,(当我走出去时,当我回来时,我的 *** 已关闭,所以我猜终端会话结束了,那些挂起的会话也结束了)。【参考方案2】:

你只需要一行:

use Parallel::ForkManager;
my $pm = Parallel::ForkManager->new( 10 );
for ( 1 .. 1000 ) 
    $pm->start and next;
    alarm 90;             # <---
    # some job for fork
    $pm->finish;

$pm->wait_all_children();

您不需要设置信号处理程序,因为您确实意味着进程终止。

如果您在孩子中exec,它甚至可以工作。它不适用于 Windows,但首先在 Windows 上使用 fork 是有问题的。

【讨论】:

这行得通,但仅在这个基本示例中 - 而不是在我的实际脚本中。请看看我在#EDIT#之后的第一篇文章 我能想到几种可能性,但除非有其他东西使用alarm,否则它们不太可能。它可以在一个模块中,例如数据库驱动程序。我会看看我可以对基于父级的解决方案做些什么。 P::FM 绝对不是这样写的。 我没有使用任何数据库驱动程序。只有 WWW::Mech、File::Slurp:Unicode、Digest::MD5、Encode、Data::Dumper 和我自己的文件操作模块,控制 WWW::Mech 和解析 html【参考方案3】:

只需在子进程内(即在$pm-&gt;start and next 和循环结束之间)执行您链接到的答案所建议的操作即可。除了与 Parallel::ForkManager 交互之外,您不需要做任何特别的事情确保您不会意外杀死父母 :)

【讨论】:

这有效,但仅在这个基本示例中 - 在我的实际脚本中没有。请看看我在#EDIT#之后的第一篇文章

以上是关于Perl,Parallel::ForkManager - 如何实现 fork 超时的主要内容,如果未能解决你的问题,请参考以下文章

Perl - 如何查看Perl模块路径

Perl 之父同意 Perl 6 改名为 Raku

以后没有 Perl 6 了!Perl 之父同意改名

Perl基础速成

Perl 的 rpm 版本不同于“perl -v”

Perl语言入门