【问题标题】:Timeout a function (windows)?超时功能(Windows)?
【发布时间】:2014-03-16 16:16:05
【问题描述】:

我正在尝试为特定功能实现超时。我检查了 SE 中的许多问题,但找不到适合我的问题的任何解决方案,因为:

  1. 我在 Windows 中运行 python
  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'

但我不确定在 python 上可以使用哪个模块来实现这一点。请注意,我是新手,我写的所有脚本都是直接基于 SE Answers 或 Python Documentation。

【问题讨论】:

    标签: python windows python-2.7 timeout


    【解决方案1】:

    我认为解决这个问题的一个好方法是创建一个装饰器并使用Thread.join(timeout=seconds) 方法。请记住,没有什么好的方法可以杀死线程,因此只要您的程序正在运行,它就会或多或少地继续在后台运行。

    首先,像这样创建一个装饰器:

    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 as e:
                        res[0] = e
                t = Thread(target=newFunc)
                t.daemon = True
                try:
                    t.start()
                    t.join(timeout)
                except Exception as 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():
        ...
    

    【讨论】:

    • 感谢您的回答。我尝试了上面的装饰器功能,它按预期工作。我必须做的唯一更改是“MyResult = timeout(timeout=16)(MyModule.MyFunc)(MyArgs)”,而不是放入 'func' 并调用它。
    • 没有问题。但是,无论哪种方式都应该有效,因为 timeout(timeout=16)(MyModule.MyFunc) 返回一个函数,然后您可以使用您的参数调用该函数(我只是没有将它们包含在答案中)。
    • 耶!终于找到了一个不包括信号的好答案(警报功能在 Windows 上不起作用)。谢谢@acushner。
    • 谢谢你,最好的人
    • 非常感谢您的回答@acushner - 很有魅力。我想知道如果您的函数中有一些特定值(例如列表)在不断更新,并且即使超时到期也想返回它们,我想知道如何处理。您(或任何人)是否对在这种情况下如何进行有线索?我猜您应该对原始函数的代码进行一些更改,但是哪个?提前致谢!
    【解决方案2】:

    @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
    

    【讨论】:

    • 当我使用此代码时,pytest 在其错误报告中产生双重输出。为什么会这样?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-08
    • 2018-05-17
    • 1970-01-01
    • 1970-01-01
    • 2014-04-22
    • 1970-01-01
    相关资源
    最近更新 更多