【发布时间】:2021-04-07 23:43:03
【问题描述】:
我正在尝试在我的 Perl 后端进程中实现警报,以便在卡住时间过长时终止。我试图实现alarm documentation page on Perldoc 上给出的代码(这是文档中的逐字记录,而不是调用我的程序的关键子例程的行而不是文档中的示例行):
eval {
local $SIG{ALRM} = sub { die "alarm\n" }; # NB: \n required
alarm $timeout;
&FaithTree::Backend::commandLine({ 'skipWidgets' => $skipWidgets, 'commandLineId' => $commandLineId, 'force' => $force });
alarm 0;
};
if ($@) {
die unless $@ eq "alarm\n"; # propagate unexpected errors
# timed out
}
else {
# didn't
}
鉴于此代码,当警报应该超时时不会发生任何事情。另一方面,如果我删除 $SIG{ALRM} 的自定义定义(同样,它直接来自 Perl 文档),警报会触发,只是没有自定义处理程序。
我想知道我使用 Thread::Queue 的事实是否在警报失败中发挥了作用,但这并不能解释为什么只要我跳过重新定义 $SIG{ALRM} 就可以工作。
这是一个带有子例程的最小可运行版本,它故意是一个用于测试的无限循环:
eval {
$SIG{ALRM} = sub { die "alarm\n" }; # NB: \n required
alarm 1;
&FaithTree::Test::Backend::commandLine({ 'skipWidgets' => $skipWidgets, 'commandLineId' => $commandLineId, 'force' => $force });
alarm 0;
};
if ($@) {
die unless $@ eq "alarm\n"; # propagate unexpected errors
# timed out
}
else {
exit;
}
package FaithTree::Test::Backend;
use File::Tail;
use threads;
use threads::shared;
use Thread::Queue;
sub commandLine {
our $N //= 4;
my $Q = new Thread::Queue;
my @kids = map threads->create( \&FaithTree::Test::Backend::fetchChild, $Q ), 1 .. $N;
my @feeds = ( "1","2","3","4" );
foreach my $feed (@feeds) {
$Q->enqueue( $feed );
}
$Q->enqueue( ( undef ) x $N );
$_->join for @kids;
}
sub fetchChild {
print "Test";
# Access queue.
my $Q = shift;
#What is my thread id?
my $tid = threads->tid();
my ($num, $num2);
for ( ; ; ){
if ($num2 == 10000) {
say STDERR $tid . ': ' . $num;
$num2 = 0;
}
$num++;
$num2++;
}
return 1;
}
如果您注释掉$SIG{ALRM} 行,它将在警报设置为超时时终止。如果你把它留在原地,它永远不会终止。
【问题讨论】:
-
Thread::Queue... 所以这是一个线程程序?在线程程序中使用信号并没有真正意义,因为信号被发送到进程(而不是线程)。您是否甚至根据threads.pm docs在主线程中设置
$SIG{ALRM}? -
@ikegami 是的。这一切都是在它分叉到不同的线程之前。每个线程都是检查内容更新的工作人员。直到您在上面看到的那个子例程调用之后才创建线程(当然,除了主进程本身)。目标是如果它在计划运行时间之外被卡住,就杀死整个事情。我可能完全基于 Thread::Queue 影响这一点;我只是想弄清楚为什么直接从文档中提取的代码不起作用。
-
我想如果我能想出一个好方法来监控 /each/ 线程的运行时间并将 Perl
->kill()发送到所述线程,如果它运行时间很长,那会更干净而不是杀死整个进程,但我只是试图做一些基本的事情来避免进程永远不会终止(并随后在下次 cron 启动它时阻塞)的情况。 -
也许看门狗设置对您来说更有意义。每隔一段时间更改 pid 文件的修改时间。下次 cron 作业运行时,如果修改时间超过了允许的时间,如果旧任务仍在运行,则终止旧任务。 [类似的事情也可以在一个线程一个线程的基础上完成。]
-
@ikegami 我已经添加了上面的代码。谢谢!看门狗是有道理的——我实际上一直在阅读关于如何最好地将一个看门狗放在一起的想法,然后遇到了一个用 Perl 编写的。我可能应该这样做,但我想,“如果我能让程序自行处理会怎样?”