2014-02-17 54 views
8

我想實現特定函數的超時。我已經檢查了許多在SE的問題,但沒有找到它適合我的問題的任何解決方案,因爲在Windows中超時python函數

  1. 我在Windows中運行蟒蛇
  2. 超時被應用上,我沒有一個Python功能控制,即在已設計的模塊中定義。
  3. Python函數是不是一個子

我有一個已經設計好的自定義模塊(說MyModule的)制定了具體任務,並有在它定義的功能。其中一個函數(比如說MyFunc)由於外部因素而傾向於永遠運行,而我不想讓python腳本掛起。

我打算增加一個超時功能,如下面說的僞

import MyModule 

    set_timeout(T) 
    MyResult=MyModule.MyFunc() 

    #Come to this part of script after execution of MyFunc() or after T seconds (the latter on priority) 
    if Timeout occurred: 
     print 'MyFunc did not execute completely' 
    else: 
     print 'MyFunc completed' 

但我不知道是哪個模塊可以用來實現這一目標的蟒蛇。請注意,我是新手,我編寫的所有腳本都直接基於SE Answers或Python文檔。

回答

13

我認爲一個很好的方法是創建一個裝飾器並使用Thread.join(timeout)方法。請記住,殺死線程沒有好方法,所以只要程序正在運行,它會繼續在後臺運行,或多或少。

第一,創建這樣一個裝飾:

from threading import Thread 
import functools 

def timeout(timeout): 
    def deco(func): 
     @functools.wraps(func) 
     def wrapper(*args, **kwargs): 
      res = [Exception('function [%s] timeout [%s seconds] exceeded!' % (func.__name__, timeout))] 
      def newFunc(): 
       try: 
        res[0] = func(*args, **kwargs) 
       except Exception, e: 
        res[0] = e 
      t = Thread(target=newFunc) 
      t.daemon = True 
      try: 
       t.start() 
       t.join(timeout) 
      except Exception, je: 
       print 'error starting thread' 
       raise je 
      ret = res[0] 
      if isinstance(ret, BaseException): 
       raise ret 
      return ret 
     return wrapper 
    return deco 

然後,做這樣的事情:

func = timeout(timeout=16)(MyModule.MyFunc) 
try: 
    func() 
except: 
    pass #handle errors here 

你可以使用這個裝飾的任何地方,你需要的東西,如:

@timeout(60) 
def f(): 
    ... 
+1

感謝您的回答。我嘗試了上面的裝飾器功能,並按預期工作。我唯一需要做的改變是「MyResult = timeout(timeout = 16)(MyModule.MyFunc)(MyArgs)」,而不是放入'func'並且調用它。 – punarvak

+0

沒有問題。儘管如此,任何方式應該工作,因爲超時(超時= 16)(MyModule.MyFunc)返回一個函數,然後你可以調用你的參數(我只是沒有包括在答案中)。 – acushner

+4

耶!最後找到了一個不包含信號的好回答(警報功能在Windows上不起作用)。謝謝@acushner。 – propjk007

0

@ acushner的答案適用於python 3.5:

from threading import Thread 
import functools 

def timeout(seconds_before_timeout): 
    def deco(func): 
     @functools.wraps(func) 
     def wrapper(*args, **kwargs): 
      res = [Exception('function [%s] timeout [%s seconds] exceeded!' % (func.__name__, seconds_before_timeout))] 
      def newFunc(): 
       try: 
        res[0] = func(*args, **kwargs) 
       except Exception as e: 
        res[0] = e 
      t = Thread(target=newFunc) 
      t.daemon = True 
      try: 
       t.start() 
       t.join(seconds_before_timeout) 
      except Exception as e: 
       print('error starting thread') 
       raise e 
      ret = res[0] 
      if isinstance(ret, BaseException): 
       raise ret 
      return ret 
     return wrapper 
    return deco