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->start and next
和循环结束之间)执行您链接到的答案所建议的操作即可。除了与 Parallel::ForkManager 交互之外,您不需要做任何特别的事情确保您不会意外杀死父母 :)
【讨论】:
这有效,但仅在这个基本示例中 - 在我的实际脚本中没有。请看看我在#EDIT#之后的第一篇文章以上是关于Perl,Parallel::ForkManager - 如何实现 fork 超时的主要内容,如果未能解决你的问题,请参考以下文章