使用警报设置读取标准输入的超时时间
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() .
【讨论】:
以上是关于使用警报设置读取标准输入的超时时间的主要内容,如果未能解决你的问题,请参考以下文章