我有一个Perl脚本可以自动从各种来源下载内容。它使用eval
在alarm
块中进行下载,以便在需要的时间过长时尝试超时:
eval {
alarm(5);
my $res = $ua->request($req);
$status = $res->is_success;
$rawContent = $res->content;
$httpCode = $res->code;
alarm(0);
};
这已经工作了多年,但在做了一些系统更新之后,它突然停止工作。相反,它击中的第一个源超时,我得到以下错误,程序终止:
Alarm clock
我做错了什么阻止eval
突然发现警报?
答案 0 :(得分:6)
SIGALRM的默认设置是终止程序,因此您需要处理它。一种常见的方法是在捕获SIGALRM时发出die
,将其变为异常,即eval
- 编辑。
eval {
local $SIG{ALRM} = sub { die "Timed out" };
alarm(5);
my $res = $ua->request($req);
$status = $res->is_success;
$rawContent = $res->content;
$httpCode = $res->code;
alarm(0);
};
if ($@ and $@ !~ /Timed out/) { die } # re-raise if it is other error
信号处理也用于Unix中的超时,虽然在
eval{}
块内安全保护,但您可以设置一个信号处理程序来捕获警报信号,然后安排在一定时间内将信号传送给您。然后尝试阻止操作,在完成后清除警报,但在退出eval{}
阻止之前清除警报。如果它关闭,你将使用die()跳出块,就像你在其他语言中使用longjmp()或throw()一样。
至于它是如何工作的,我能想到的一件事是eval
中使用的包有自己的定时器,基于alarm
,因此取消了你的alarm
。来自alarm
一次只能计算一个计时器。每次调用都会禁用前一个定时器,并且可以提供一个参数0来取消之前的定时器而不启动新的定时器。
他们可能在超时时抛出异常并且您有预期的行为。此更新中的包行为已更改,现在您的警报正常工作并需要处理。当然,这是猜测。