如果我设置 $SIG{ALRM},警报似乎不会触发
alarm does not seem to fire if I set $SIG{ALRM}
我正在尝试在我的 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}
行,当闹钟设置超时时,它会终止。如果你把它留在原地,它永远不会终止。
信号和线程不能很好地混合。您可能需要重新考虑对信号的使用。例如,您可以将所有线程内容移动到子进程。
信号处理程序仅在 Perl 操作之间调用。主线程正在调用XS sub thread->join
,信号处理函数会被调用一次join
returns.
大多数阻塞系统调用都可以被中断(返回错误 EINTR
),因此可以编写 join
的信号感知版本。除了我似乎记得 pthread 函数是不可中断的,所以也许不是。
在这种特殊情况下,您可以让线程在结束时向主线程发出信号,使用允许主线程阻塞直到信号发生或超时发生的系统。 cond_signal
/cond_timedwait
就是这样的系统。
use Sub::ScopeFinalizer qw( scope_finalizer );
use Time::HiRes qw( time );
my $lock :shared;
my $threads_remaining = $N;
my $Q = new Thread::Queue;
my @threads;
{
lock $lock;
for (1..$N) {
++$threads_remaining;
push @threads, async {
my $guard = scope_finalizer {
lock $lock;
--$threads_remaining;
cond_signal($lock);
};
worker($Q);
}
}
}
my $max_end_time = time + 1;
# ...
{
lock $lock;
while ($threads_remaining) {
if (!cond_timedwait($lock, $max_end_time)) {
# ... Handle timeout ...
}
}
}
$_->join for @threads;
我正在尝试在我的 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}
行,当闹钟设置超时时,它会终止。如果你把它留在原地,它永远不会终止。
信号和线程不能很好地混合。您可能需要重新考虑对信号的使用。例如,您可以将所有线程内容移动到子进程。
信号处理程序仅在 Perl 操作之间调用。主线程正在调用XS sub thread->join
,信号处理函数会被调用一次join
returns.
大多数阻塞系统调用都可以被中断(返回错误 EINTR
),因此可以编写 join
的信号感知版本。除了我似乎记得 pthread 函数是不可中断的,所以也许不是。
在这种特殊情况下,您可以让线程在结束时向主线程发出信号,使用允许主线程阻塞直到信号发生或超时发生的系统。 cond_signal
/cond_timedwait
就是这样的系统。
use Sub::ScopeFinalizer qw( scope_finalizer );
use Time::HiRes qw( time );
my $lock :shared;
my $threads_remaining = $N;
my $Q = new Thread::Queue;
my @threads;
{
lock $lock;
for (1..$N) {
++$threads_remaining;
push @threads, async {
my $guard = scope_finalizer {
lock $lock;
--$threads_remaining;
cond_signal($lock);
};
worker($Q);
}
}
}
my $max_end_time = time + 1;
# ...
{
lock $lock;
while ($threads_remaining) {
if (!cond_timedwait($lock, $max_end_time)) {
# ... Handle timeout ...
}
}
}
$_->join for @threads;