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

时间:2012-06-10 18:04:28

标签: perl parallel-processing timeout fork

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

Basic 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();

我想限制时间为“#for fork for fork”。例如,如果它没有在90秒内完成。那么它(fork)应该被杀死/终止。 我想过using this,但我不得不说,我不知道如何在Parallel :: ForkManager中使用它。

修改

感谢hobbs和ikegami。你的建议都有效.....但只是在这个基本的例子中,而不是在我的实际脚本中:(。 screenshot 这些叉子将永远存在 - 说实话 - 我不知道为什么。我使用这个脚本几个月。没有改变任何东西(虽然许多事情取决于外部变量)。 每个fork都必须从网站下载页面,解析它并将结果保存到文件中。每叉不应超过30秒。超时设置为180秒。那些悬挂叉是完全随机的,因此很难追踪问题。这就是为什么我想出一个简单的临时解决方案 - 超时&杀死。

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

编辑2

其中一个分叉,悬挂1小时38分钟并返回“超时PID” - 这是我在die() alarm()中输入的内容。所以超时工作...但它的晚期大约1小时36分钟;)。你有什么想法吗?

3 个答案:

答案 0 :(得分:8)

<强>更新

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

如,

$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 $SIG{TERM} handler for graceful shutdown!)
    ...
    $pm->finish;
  }

  $pm->wait_all_children;

}

(正如其他人所说,最好让孩子通过alarm()自我调节,但这对你来说似乎是间歇性的。你也可以采取浪费,粗暴的行为,比如让每个孩子自己fork() or exec('bash', '-c', 'sleep 90; kill -TERM $PPID') 。)

答案 1 :(得分:4)

您只需要一行:

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首先是有问题的。

答案 2 :(得分:1)

在子进程内部(即在$pm->start and next和循环结束之间)执行你所链接的答案建议。你需要做什么才能使它与Parallel :: ForkManager进行交互,除了确保你不会意外杀死父母:)