Python中線程Timeout的使用

Python中關於Timeout有另一種用起來更簡便的方法,即使用裝飾器。這種方式是使用sys模塊的settrace等方法重構了python的threading類:

#!/usr/bin/python  import threading  import sys  class KThread(threading.Thread):      """Subclass of threading.Thread, with a kill() method."""      def __init__(self, *args, **kwargs):          threading.Thread.__init__(self, *args, **kwargs)          self.killed = False      def start(self):          """Start the thread."""          self.__run_backup = self.run          """Force the Thread to install our trace."""          self.run = self.__run          threading.Thread.start(self)      def __run(self):          """Hacked run function, which installs the trace."""          sys.settrace(self.globaltrace)          self.__run_backup()          self.run = self.__run_backup      def globaltrace(self, frame, why, arg):          if why == 'call':              return self.localtrace          else:              return None      def localtrace(self, frame, why, arg):          if self.killed:              if why == 'line':                  raise SystemExit()          return self.localtrace      def kill(self):          self.killed = True

然後,構造一個timeout裝飾器,這個裝飾器利用上面重載的KThread實現超時限制:

def timeout(seconds):      def timeout_decorator(func):          def _new_func(oldfunc, result, oldfunc_args, oldfunc_kwargs):              result.append(oldfunc(*oldfunc_args, **oldfunc_kwargs))          def _(*args, **kwargs):              result = []              '''create new args for _new_funcbecause                 we want to get the func return val to result list              '''              new_kwargs = {                  'oldfunc': func,                  'result': result,                  'oldfunc_args': args,                  'oldfunc_kwargs': kwargs              }              thd = KThread(target=_new_func, args=(), kwargs=new_kwargs)              thd.start()              thd.join(seconds)              alive = thd.isAlive()              '''kill the child thread'''              thd.kill()              if alive:                  alert_exce = u'function timeout for [%d s].' % seconds                  raise Timeout(alert_exce)              else:                  return result[0]          _.__name__ = func.__name__          _.__doc__ = func.__doc__          return _      return timeout_decorator

  這種方法使用起來十分簡單:只需要在需要超時控制的函數前面使用@timeout(sec)裝飾器即可。

  但是這種方法有比較明顯的缺陷,因為其本質是使用將函數使用重載的線程來控制,一旦被添加裝飾器的函數內部使用了線程或者子進程等複雜的結構,而這些線程和子進程其實是無法獲得超時控制的,所以可能導致外層的超時控制無效。