我有一个函数可以调用其他几个函数,例如
sub do_work {
send_mail();
send_soap_envelope();
send_rpc();
}
被调用的函数可能会挂起,所以我希望在超时后停止它们。我想避免分叉,因为它在我的上下文中很昂贵(例如,在每个fork之后需要重新创建数据库句柄)。我想出了以下方案:
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子程序。我的直觉是对的吗?
答案 0 :(得分:5)
自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)/;
* - 为了加快程序的速度,现在检查的次数会少一些,但你不应该注意到它们之间的区别。
答案 1 :(得分:1)
它不是完全安全,因为它在安装alarm()
处理程序之前调用SIGALRM
。交换local $SIG{ALRM}
和alarm
行,应该会有很大的改进。
答案 2 :(得分:0)