我有一个函数可以调用其他几个函数,例如
sub do_work {
send_mail();
send_soap_envelope();
send_rpc();
}
被调用的函数可能会挂起,所以我想在超时后停止它们。我想避免分叉,因为它在我的上下文中很昂贵(例如,每次分叉后都需要重新创建数据库句柄)。我想出了以下方案:
sub timeout {
my ($code) = @_;
eval {
alarm 2;
local $SIG{ALRM} = sub { die 'timeout' };
&$code;
alarm 0;
};
# handling of $@ eq 'timeout' removed for brevity
}
sub do_work {
timeout &send_mail;
timeout &send_soap_envelope;
timeout &send_rpc;
};
timeout()
函数(在此示例中硬编码为 2 秒超时)使用 eval
块作为使用 die
中止有效负载函数执行的方法。
这在我的测试场景中工作正常,但是我对如果die
在Perl解释器不处于"安全状态"时中断有效负载功能会发生什么感到不安,例如,当它正在处理XS子例程时。我的直觉对吗?
从 5.8.1 开始,Perl 使用了"安全信号处理"。它不会向系统提供信号处理程序,而是提供安全的信号处理程序。此安全信号处理程序只是记录信号已接收并返回。在执行 Perl 操作码之间,解释器检查是否接收到任何信号,如果有,则调用信号处理程序。
这意味着信号不会在长操作过程中得到处理,例如长 XS 调用或长正则表达式匹配。信号会中断大多数系统调用,因此,如果您处于阻塞系统调用的中间(例如 sleep
、read
等)
alarm(2);
my $s = time;
$SIG{ALRM} = sub {
my $e = time;
print $e-$s, "n"; # 6, not 2.
};
('a' x 25) =~ /a*a*a*a*a*a*a*a*a*(?:b|c)/;
* — 为了加快程序速度,现在检查的频率会降低一些,但您不应该注意到差异。
它不太安全,因为它在安装SIGALRM
处理程序之前调用alarm()
。交换local $SIG{ALRM}
和alarm
线,应该会有很大的改进。
好的,现在我看到perldoc -f alarm
提到了我的确切用例:
如果要使用"警报"使系统调用超时,则需要使用"eval"/"die"对。
(后面是示例代码。