2013-07-23 34 views
3

我有這樣的代碼:使用報警設置超時讀取標準輸入

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

它是正確的嗎? 可能是8到9線之間的比賽條件?

回答

2

讓我們來看看,這是怎麼回事:

my ($timeout, $size, $buffer) = (10, 10, undef); 
eval { 
    #establish ALRM signal handler 
    local $SIG{ALRM} = 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"; 
}; 

$ @設置如果字符串是EVAL-ED沒編譯,或者Perl代碼評估模過程中執行的()d。在這種情況下$ @的值是編譯錯誤,或參數死:

if ([email protected]) {  
    warn [email protected]; 
} 

所以,這將打印die消息「報警\ n」如果我們沒有在10從sysread執行返回第二。

在很偶然的情況下,當輸入將獲得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 $SIG{ALRM} = 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

+0

在「my $ nread = sysread STDIN,$ buffer,$ size;」之後和「報警0」之前。這裏!。可能是我們的警報已過並且交付了SIGALARM。 – drlexa

+0

@drlexa現在就來看看。好,我寫了關於發生了什麼的仔細解釋。現在調查問題。 – user4035

+0

@drlexa回答更新 – user4035

0

你在運行這個操作系統?什麼版本的Perl?

適用於Mac OS X 10.8.3,適用於Perl 5.12.4。

如果您在Windows上使用perl,您會發現信號在POSIX和POSIX類操作系統上的工作方式不同,您可能需要使用select()的四參數版本。代替。

1

你的alarm使用引入了一個潛在的競爭條件。

正常的解決辦法是在你的eval塊後加alarm 0;,所以如果第一個alarm 0沒有執行,你仍然可以關閉報警。

或者你可以使用CPAN上的Time::Out包來包裝你的代碼,它更好,更安全。