Perl, Parallel:: ForkManager - как реализовать тайм-аут для fork

можно ли реализовать какой-то тайм-аут (ограничение по времени) для fork с помощью Parallel::ForkManager ?

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

Я хотел бы ограничить время для "# некоторая работа для вилки". Например, если его не закончили в 90 секунд. затем он (вилка) должен быть убит/прекращен. Я думал о используя этот но я должен сказать, что я не знаю, как использовать его с Параллель:: ForkManager.

редактировать

спасибо Хоббс и Икегами. Оба ваших предложения сработали..... но только в этом базовом примере, а не в моем фактическом скрипте : (. screenshot Эти вилки останутся там навсегда , и, честно говоря, я не знаю почему. Я использую этот сценарий пару месяцев. Ничего не изменилось (хотя многое зависит от внешних переменных). Каждая вилка должна загрузить страницу с веб-сайта, проанализировать ее и сохранить результаты в файл. Это не должно занять больше чем 30 секунд на развилке. Время ожидания составляет 180 секунд. Эти висячие вилки полностью случайны, поэтому очень трудно проследить проблему. Вот почему я придумал временное, простое решение-timeout & kill.

что может отключить (прервать) ваши методы тайм-аута в моем коде ? У меня нет других alarm() в любом месте в моем коде.

правка 2

одна из вилок, висела для 1h38m и вернула "timeout PID" - это то, что я набираю в die() для alarm(). Так что тайм-аут... но поздно о 1х36,5м ;). У тебя есть какие-нибудь идеи?

3 ответов


обновление

извините за обновление после закрытия, но я был бы упущен, если бы не указал, что Parallel:: ForkManager также поддерживает run_on_start обратный. Это можно использовать для установки функции "регистрация ребенка", которая заботится о time()-штемпелевать PIDs для вас.

Е. Г.,

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

в результате получается, что в сочетании с run_on_wait как описано ниже, основной цикл P:: FM не должен делать ничего особенного. То есть она может оставаться простой $pm->start and next, и обратные вызовы позаботятся обо всем остальном.

Оригинальный Ответ

параллель:: обработчик,и немного бухгалтерии, может заставить висеть и детей 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').)


все, что вам нужно, это одна строчка:

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 является сомнительным в первую очередь.


просто сделайте то, что предлагает ответ, с которым вы связаны, внутри дочернего процесса (т. е. между $pm->start and next и конец цикла. Нет ничего особенного, что вам нужно сделать, чтобы он взаимодействовал с Parallel:: ForkManager, кроме как убедиться, что вы случайно не убьете родителя:)