【问题标题】:Signal handler not working with double eval信号处理程序不适用于双重评估
【发布时间】:2015-04-10 07:00:53
【问题描述】:

我有这段代码可以让一个长时间运行的进程超时(在这种情况下为sleep):

#!/usr/bin/env perl
use strict;
use warnings;

die "Usage: $0 SLEEP TIMEOUT\n" unless @ARGV == 2;
my ( $sleep, $timeout ) = @ARGV;

$|++;

eval {
    local $SIG{ALRM} = sub { die "TIMEOUT\n" };
    alarm $timeout;

    eval {
        # long-running process
        print "Going to sleep ... ";
        sleep $sleep;
        print "DONE\n";
    };  

    alarm 0;    # cancel timeout
};  

die $@ if $@;

当我以./alarm 5 2 运行它时,我希望它会die 说“超时”。但是它以 0 退出并且什么也没说。当我删除内部eval 块(不是块的内容,只是eval)时,它按预期工作。有人可以解释这是为什么吗?谢谢。

【问题讨论】:

  • perldoc -f alarm:混用“alarm”和“sleep”调用通常是错误的,因为“sleep”可能在您的系统内部与“alarm”一起实现。
  • 混用 "alarm" 和 :"sleep" 调用很少会出错,因为在内部实现 "sleep" 和 "alarm" 的系统并不常见,而且越来越多。

标签: perl


【解决方案1】:

因为您在第一个eval 块中捕获错误,而第二个eval 块没有异常并清除$@

eval {
    local $SIG{ALRM} = sub { die "TIMEOUT\n" };
    alarm $timeout;

    eval {
        # long-running process
        print "Going to sleep ... ";
A:      sleep $sleep;
        print "DONE\n";
    };  
B:

    alarm 0;    # cancel timeout
C:};  

die $@ if $@;

$sleep > $timeout,所以在A:,你的程序会抛出一个SIGALRM。该信号被您的本地信号处理程序捕获并调用die "TIMEOUT\n"。因此 Perl 将 $@ 设置为 "TIMEOUT\n" 并在 B: 处恢复执行。然后,您的程序会到达C:,而不会出现任何其他错误。由于您的外部eval 块正常完成,Perl 清除$@,并且您的最终die 语句不会执行。

要做你想做的事,你也可以

  1. 不要在外层使用eval
  2. 在外部块的末尾添加另一个 die $@ if $@ 调用

【讨论】:

  • 对。我在Camel Book 中发现了这一点:“如果没有错误,$@ 保证设置为空字符串,因此您可以在之后可靠地测试错误。”
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-05-29
  • 2012-08-19
  • 1970-01-01
  • 2015-07-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多