【问题标题】:alarm Escaping Perl 'eval' block警报转义 Perl 'eval' 块
【发布时间】:2016-06-14 16:07:49
【问题描述】:

我有一个自动从各种来源下载内容的 Perl 脚本。它使用alarmeval 块中进行下载,这样如果时间过长,尝试就会超时:

eval {
    alarm(5);
    my $res = $ua->request($req);
    $status = $res->is_success;
    $rawContent = $res->content;    
    $httpCode = $res->code;
    alarm(0);       
};

这已经工作了多年,但是在进行了一些系统更新后,它突然停止工作。相反,它遇到的第一个源超时,我收到以下错误并且程序终止:

 Alarm clock

我做错了什么导致eval 无法突然收到警报?

【问题讨论】:

  • “一些系统更新” - 需要说明吗?
  • 如果你使用LWP::UserAgent,那么最好设置$ua->timeout(5)而不是使用alarm
  • “这已经工作了很多年,但是在做了一些系统更新之后,它突然停止工作了” 我想@Amias 会对此感兴趣
  • @tjd 我使用 yum 和 cPanel/WHM(它是一个网络托管环境)进行了大量更新。不幸的是,我没有所有更新的列表。
  • @Borodin,我不记得我第一次写这篇文章时使用警报的原因。也许我应该再探索一下。

标签: perl eval lwp


【解决方案1】:

SIGALRM 的默认值是终止程序,所以你需要处理它。一种常见的方法是在捕获到 SIGALRM 时发出die,将其变成异常,即eval-ed。

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

来自Signals in perlipc

信号处理也用于 Unix 中的超时,虽然在 eval{} 块中受到安全保护,但您设置一个信号处理程序来捕获警报信号,然后安排在几秒钟内将一个信号发送给您。然后尝试您的阻止操作,在完成时清除警报,但在您退出 eval{} 阻止之前清除警报。如果它关闭,您将使用 die() 跳出块,就像您在其他语言中使用 longjmp() 或 throw() 一样。


至于它是如何工作的,我能想到的一件事是eval 内部使用的包有自己的计时器,基于alarm,因此取消了您的alarm。来自alarm

一次只能有一个计时器在计时。每次调用都会禁用前一个计时器,并且可以提供一个参数 0 来取消前一个计时器而不启动新计时器。

他们可能会在超时时抛出异常,而您有预期的行为。此软件包行为在更新中发生了变化,现在您的警报可以正常工作并需要处理。当然,这是一个猜测。

【讨论】:

  • 成功了——程序重新焕发生机!谢谢你,@zdim!知道为什么它直到现在都不会造成问题吗?
  • @TimothyR.Butler 我现在正在为这个问题摸不着头脑......直到现在你有什么?或者——eval 里面的代码有变化吗?一个人不应该同时使用多个闹钟,也不应该通过它们实现任何其他调用(如睡眠)。
  • @TimothyR.Butler 我能想到的最好的就是eval 中使用的包由于更新而改变了它们的行为。他们可能有自己的定时器来取消你的闹钟,但现在他们没有。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-06-13
  • 2013-06-07
  • 2011-09-14
  • 2021-06-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多