使用警报设置读取标准输入的超时时间

Posted

技术标签:

【中文标题】使用警报设置读取标准输入的超时时间【英文标题】:Use alarm to set a timeout for reading stdin 【发布时间】:2013-07-23 08:30:03 【问题描述】:

我有这个代码:

#!/usr/bin/perl
use strict;
use warnings;
my ($timeout, $size, $buffer) = (10, 10, undef);
eval 
    local $SIGALRM = sub  die "alarm\n" ; # NB: \n required
    alarm $timeout;
    my $nread = sysread STDIN, $buffer, $size;
    # !!!! race condition !!!!!
    alarm 0;
    print "$nread: $buffer";
;
if ($@)     
    warn $@;

正确吗? 可能在 8 到 9 行之间存在竞态条件吗?

【问题讨论】:

【参考方案1】:

让我们看看,发生了什么:

my ($timeout, $size, $buffer) = (10, 10, undef);
eval 
    #establish ALRM signal handler
    local $SIGALRM = sub  die "alarm\n" ; # NB: \n required

    #send alarm signal to program in 10 second
    alarm $timeout;

    #try yo read 10 bytes of data into $buffer
    my $nread = sysread STDIN, $buffer, $size;

    #cancel the previous timer without starting a new one 
    #if we returned from sysread. Yes, if 10 seconds pass
    #before the next function is executed, the script will
    #die even though the data was read
    alarm 0;

    #print number of bytes read (will be 10) and the string,
    #read from input
    print "$nread: $buffer";
;
如果要评估的字符串没有编译,或者如果在评估期间执行的 Perl 代码 die()d,则设置

$@。在这些情况下,$@ 的值是编译错误,或者是要死的参数:

if ($@)     
    warn $@;

所以,如果我们没有在 10 秒内从 sysread 返回,这将打印出消息“alarm\n”。

在极不可能的情况下,当输入将在 10 秒过去之前收到并且我们将无法运行警报 0;,我建议使用以下代码:

my ($timeout, $size, $buffer) = (10, 10, undef);

#I define $nread before the signal handler as undef, so if it's defined
#it means, that sysread was executed and the value was assigned
my $nread = undef;
eval 
    local $SIGALRM = sub 

        #if it's not defined - it means, that sysread wasn't executed
        unless(defined($nread))
        
            die "alarm\n";
        
    ;
    alarm $timeout;
    $nread = sysread STDIN, $buffer, $size;
    alarm 0;
    print "$nread: $buffer";
;

不幸的是,当赋值运算符没有被执行时,它并没有让我们摆脱这种情况。

链接:

http://perldoc.perl.org/functions/alarm.html

http://perldoc.perl.org/perlvar.html

http://perldoc.perl.org/functions/sysread.html

【讨论】:

在“my $nread = sysread STDIN, $buffer, $size;”之后在“警报 0;”之前。这里!。可能是我们的警报已经过去,SIGALARM 已交付。 @drlexa 立即查看。很好,我写了关于发生了什么的仔细解释。现在正在调查问题。【参考方案2】:

您对alarm 的使用引入了潜在的竞争条件。

正常的解决方案是在你的eval块之后添加alarm 0;,所以如果第一个alarm 0没有被执行,你仍然可以关闭警报。

或者你可以使用 CPAN 上的Time::Out 包来包装你的代码,这样更好更安全。

【讨论】:

【参考方案3】:

您在什么操作系统上运行它?什么版本的 perl?

在 Mac OS X 10.8.3 和 perl 5.12.4 上运行良好。

如果您在 Windows 上使用 perl,您会发现信号在 POSIX 和类似 POSIX 的操作系统上的工作方式不同,您可能需要改用 4 参数版本的 select() .

【讨论】:

以上是关于使用警报设置读取标准输入的超时时间的主要内容,如果未能解决你的问题,请参考以下文章

如何在不读取标准输入的情况下使用“npm login”设置 npm 凭据?

使用 Qt 从标准输入异步读取

从标准输入读取整数

如何连续读取标准输入? [复制]

从文件或标准输入读取

C/C++ 服务器,通过标准输入/标准输出与客户端通信(阻塞标准输入,直到读取了多个字节)