2

我有一个调用其他几个函数的函数,例如

sub do_work {
    send_mail();
    send_soap_envelope();
    send_rpc();
}

被调用的函数可能会挂起,所以我想在超时后停止它们。我想避免分叉,因为它在我的上下文中很昂贵(例如,每次分叉后都需要重新创建数据库句柄)。我想出了以下方案:

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.

这在我的测试场景中工作得很好,但是我对如果在diePerl 解释器不处于“安全状态”时中断有效负载函数会发生什么感到不安,例如,当它正在处理 XS 子例程时。我的直觉对吗?

4

3 回答 3

5

从 5.8.1 开始,Perl 使用“安全信号处理”。它不会将您的信号处理程序提供给系统,而是提供安全的信号处理程序。这个安全的信号处理程序只是简单地记录一个信号被接收并返回。在执行 Perl 操作码之间,解释器检查是否接收到任何信号,如果有则调用您的信号处理程序。

这意味着信号不会在长操作的中间得到处理,例如长 XS 调用或长正则表达式匹配。信号会中断大多数系统调用,因此如果您处于阻塞系统调用的中间(例如,等),您的信号处理程序将在信号进入后不久sleepread调用

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)/;

* — 为了加快程序的速度,现在检查的频率有所降低,但您应该不会注意到差异。

于 2012-06-13T05:59:58.277 回答
1

不太安全,因为它alarm()在安装SIGALRM处理程序之前调用。交换local $SIG{ALRM}alarm线,应该会有很大的改进。

于 2012-06-13T17:44:29.883 回答
0

好的,现在我看到它perldoc -f alarm提到了我的确切用例:

如果你想使用“alarm”来让系统调用超时,你需要使用“eval”/“die”对。

(后面是示例代码。)

于 2012-06-13T06:08:05.703 回答