Python中线程Timeout的使用

发布时间:2019-09-15 10:02:34编辑:auto阅读(2852)

    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)装饰器即可。

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

关键字