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)裝飾器即可。

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章