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.
редактировать
спасибо Хоббс и Икегами. Оба ваших предложения сработали..... но только в этом базовом примере, а не в моем фактическом скрипте : (. Эти вилки останутся там навсегда , и, честно говоря, я не знаю почему. Я использую этот сценарий пару месяцев. Ничего не изменилось (хотя многое зависит от внешних переменных). Каждая вилка должна загрузить страницу с веб-сайта, проанализировать ее и сохранить результаты в файл. Это не должно занять больше чем 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
, и обратные вызовы позаботятся обо всем остальном.
Оригинальный Ответ
параллель::
обратный вызов, зарегистрированный этой функцией, может выполняться периодически, в то время как $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, кроме как убедиться, что вы случайно не убьете родителя:)