Perl中实现超时的方法?

21

我经常使用以下模式来设置Perl中特定代码片段的运行时间上限:

my $TIMEOUT_IN_SECONDS = 5;
eval {
    local $SIG{ALRM} = sub { die "alarm\n" };
    alarm($TIMEOUT_IN_SECONDS);
    # do stuff that might timeout.
    alarm(0);
};
if ($@) {
    # handle timeout condition.
}

我的问题:

  • 这是正确的方法吗?
  • 是否存在任何情况下,运行时间可能超过$TIMEOUT_IN_SECONDS,或者上述方法是完全可靠的?

1
你的 eval 存在一个严重的漏洞。请阅读 Try::Tiny 文档以获取详细解释:http://search.cpan.org/perldoc?Try::Tiny - daotoad
3个回答

阿里云服务器只需要99元/年,新老用户同享,点击查看详情
10
您可能需要查看Sys::SigAction。我个人没有使用过它,但是有些好评。 需要注意的一件事是,如果“可能超时的任务”本身使用了sleepalarm,请特别小心。此外,在错误处理代码中,我假设您已准备好处理除超时之外的其他错误。

请问您能否解释一下应用程序自身使用sleep的情况?以及如何解决它? - Validus Oculus

5
你也可以尝试使用Time::Out。我喜欢它的语法,它支持嵌套超时。

1
+1 这个模块会处理所有关于嵌套超时等的问题,因此您不必担心所有边缘情况。它就像 try::tiny 对 eval 一样对待 alarm。 - mikew
Time::Out模块是线程安全的吗?在我的情况下,我需要在应用程序中每个线程运行一个超时。 - reedog117
唯一的问题是它使用了 Time::HiRes::alarm,这在 Windows 上不兼容。 - mvsjes2

2

在处理信号时要小心。Perl异步接收信号,如果在回调处理另一个信号时接收到信号,则可能会丢失或相互干扰。

事件处理库在Perl中的Win32支持一般般(我需要支持非cygwin Win32),因此我通常使用简单的轮询循环来处理超时:

use Time::HiRes qw(sleep);

sub timeout {
  my $timeout = shift;
  my $poll_interval = shift;
  my $test_condition = shift;
  until ($test_condition->() || $timeout <= 0) {
    $timeout -= $poll_interval;
    sleep $poll_interval;
  }
  return $timeout > 0; # condition was met before timeout
}

my $success = timeout(30, 0.1, \&some_condition_is_met);
睡眠计时器可以轻松地由用户或调用者配置,除非您正在执行极其紧密的循环或有多个调用者等待循环(这可能会导致竞争或死锁),否则它是一种简单、可靠且跨平台的实现超时的方法。 还要注意,循环开销意味着您无法绝对保证观察到超时。$test_condition、减量、垃圾回收等都可能会干扰。

1
这并没有回答问题。这段代码无法达到与原帖中代码相同的目标。如果 some_condition_is_met() 永远不返回,程序将会挂起。 - Will Sheppard

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,