2014-10-05 75 views
2

我試圖超時運行超過3秒的功能(例如)。我正在使用信號和警報,但警報從不會觸發。我想要一個適用於任何函數的超時機制。作爲我面臨的問題的一個例子:報警信號在無限循環中未觸發

import signal 

def foobar(): 
    x = 42 
    while x >= 20: 
     if x >= 40: 
      x = 23 
    return x 

def handle_alarm(*args): 
    print("Alarm raised") 
    raise TimeoutException("timeout reached") 

signal.signal(signal.SIGALRM, handle_alarm) 
signal.alarm(3) 

try: 
    print(foobar()) 
except: 
    print("Exception Caught") 

當運行時,我的程序運行到永遠,我的處理程序永遠不會運行。任何想法爲什麼這是這種情況?另外,如果我從foobar中刪除if語句,那麼鬧鐘會觸發。

+0

可能是一個python的bug。這在pypy下工作。我認爲你應該在錯誤跟蹤器上提交這個文件。 – simonzack 2014-10-05 05:22:20

+0

你在使用什麼操作系統?視窗? – Ewan 2014-10-05 06:23:10

+0

@Ewan'SIGALRM'只存在於linux上。 – simonzack 2014-10-05 06:45:28

回答

2

在我的系統中,Mac OS X帶有MacPorts,我用很多版本的Python測試了你的代碼。展示「bug」的唯一版本是2.7。超時在2.4,2.5,2.6,3.3和3.4中工作。

現在,爲什麼會發生這種情況,並且可以做些什麼呢?

我認爲這是因爲你的foobar()是一個嚴密的循環,永遠不會「控制」回到Python的主循環。它只是運行得儘可能快,沒有任何有用的工作,但阻止了Python處理信號。

這將有助於瞭解* nix中的信號通常如何處理。由於少數庫函數是「異步信號安全」,因此在C信號處理程序中不能直接完成很多工作。 Python需要調用用Python編寫的信號處理程序,但它不能直接在使用C註冊的信號處理程序中執行該操作。因此,程序在其信號處理程序中執行的典型操作是設置一些標誌來指示信號已經收到,然後返回。然後在主循環中檢查該標誌(直接或通過使用可在信號處理程序中寫入的「管道」和select()編輯)。

所以我會假設Python主循環正在愉快地執行你的foobar()函數,並且有一個信號進來,它設置了一些內部狀態以知道它需要處理該信號,然後等待foobar()結束,或者失敗,至少對於foobar()來調用一些可中斷功能,如sleep()print()。實際上,如果您添加了睡眠(任意時間)或print聲明到foobar()的循環,您將在Python 2.7(以及其他版本)中獲得所需的超時。

無論如何,在忙碌的環路中進行短暫的睡眠總是一個好主意,以便「放鬆」它們,從而幫助安排可能需要做的其他工作。你不必在每次迭代時都睡覺 - 在這種情況下,通過循環每1000次只需要一個微小的睡眠就可以正常工作。