7

是否可以使用 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 秒内完成。那么它(叉子)应该被杀死/终止。我考虑过使用它,但我不得不说,我不知道如何将它与 Parallel::ForkManager 一起使用。

编辑

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

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

编辑 2

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

4

3 回答 3

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')。)

于 2012-06-11T04:22:17.133 回答
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,但fork首先在 Windows 上使用是有问题的。

于 2012-06-10T18:36:11.413 回答
1

只需在子进程内部(即在循环结束和循环结束之间)执行您链接到的答案建议$pm->start and next。除了确保您没有不小心杀死了父母:)

于 2012-06-10T18:14:15.670 回答