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 = Falsedef start(self):"""Start the thread."""self.__run_backup = self.run"""Force the Thread to install our trace."""self.run = self.__runthreading.Thread.start(self)def __run(self):"""Hacked run function, which installs the trace."""sys.settrace(self.globaltrace)self.__run_backup()self.run = self.__run_backupdef globaltrace(self, frame, why, arg):if why == 'call':return self.localtraceelse:return Nonedef localtrace(self, frame, why, arg):if self.killed:if why == 'line':raise SystemExit()return self.localtracedef 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_funcbecausewe 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].' % secondsraise Timeout(alert_exce)else:return result[0]_.__name__ = func.__name___.__doc__ = func.__doc__return _return timeout_decorator

  这种方法使用起来十分简单:只需要在需要超时控制的函数前面使用@timeout(sec)装饰器即可。

  但是这种方法有比较明显的缺陷,因为其本质是使用将函数使用重载的线程来控制,一旦被添加装饰器的函数内部使用了线程或者子进程等复杂的结构,而这些线程和子进程其实是无法获得超时控制的,所以可能导致外层的超时控制无效。