2016-06-14 28 views
1

我有一個Perl腳本,可以自動從各種來源下載內容。它在一個eval塊下載與alarm,這樣的嘗試將超時,如果時間過長:異常轉義Perl'eval'塊

eval { 
    alarm(5); 
    my $res = $ua->request($req); 
    $status = $res->is_success; 
    $rawContent = $res->content;  
    $httpCode = $res->code; 
    alarm(0);  
}; 

這已經工作多年了,但做了一些系統更新後,一下子就退出加工。相反,它擊中那個時代的時候,我得到以下錯誤的第一來源和程序終止:

Alarm clock 

我在做什麼錯誤,導致無法從eval追趕報警突然?

+2

「有些系統更新」 - 謹慎闡明? – tjd

+2

如果您使用[LWP :: UserAgent](https://metacpan.org/pod/LWP::UserAgent),那麼最好設置'$ ua-> timeout(5)'而不是使用['alarm '](http://perldoc.perl.org/functions/alarm.html) – Borodin

+0

*「這已經運行了多年,但在做了一些系統更新之後,突然它退出了工作」*我認爲@Amias將會對此感興趣 – Borodin

回答

6

SIGALRM的默認值是終止程序,所以你需要處理它。常見的方法是在捕獲SIGALRM時發出die,將其變爲異常,即eval -ed。

eval { 
    local $SIG{ALRM} = sub { die "Timed out" }; 
    alarm(5); 
    my $res = $ua->request($req); 
    $status = $res->is_success; 
    $rawContent = $res->content;  
    $httpCode = $res->code; 
    alarm(0);  
}; 
if ([email protected] and [email protected] !~ /Timed out/) { die } # re-raise if it is other error 

Signals in perlipc

的信號處理也可用於在Unix的超時,而一個eval{}塊內的安全保護,你的信號處理程序中設置陷阱的報警信號,然後安排有一個交付給你在幾秒鐘內。然後嘗試阻止操作,在完成時清除警報,但在您退出eval{}區塊之前不要清除警報。如果它結束,你將使用die()跳出該塊,就像你可能在其他語言中使用longjmp()或throw()一樣。


至於它是如何工作過,有一件事我能想到的是,裏面eval使用的包都有自己的計時器,基於alarm,從而消除了你的alarm。從alarm

只有一個計時器可能一次計數。每次調用都會禁用前一個定時器,並且可以提供一個0的參數來取消前一個定時器,而不必啓動一個新的定時器。

它們可能在超時時發生異常並且您有預期的行爲。此包行爲在更新中發生了變化,現在您的警報正常工作並需要處理。當然這是一個猜測。

+0

工作正常 - 程序重新開始!謝謝@zdim!任何想法爲什麼它永遠不會造成一個問題,直到現在呢? –

+0

@ TimothyR.Butler我現在正在爲此撓頭......你到現在爲止在那裏有什麼?或者 - eval中的代碼是否改變了?一個人不應該同時使用多個鬧鐘,也不應該通過他們實現任何其他的呼叫(如睡眠)。 – zdim

+0

@ TimothyR.Butler我能想到的最好的方法是在'eval'中使用的包由於更新而改變了他們的行爲。他們可能有自己的定時器取消了你的鬧鐘,現在他們沒有。 – zdim