【问题标题】:Time out decorator on a multprocessing function多处理功能上的超时装饰器
【发布时间】:2012-01-26 20:20:21
【问题描述】:

这个装饰器直接取自我在网上找到的一个例子:

class TimedOutExc(Exception):
    pass


def timeout(timeout):
    def decorate(f):
        def handler(signum, frame):
            raise TimedOutExc()

        def new_f(*args, **kwargs):

            old = signal.signal(signal.SIGALRM, handler)
            signal.alarm(timeout)

            try:
                result = f(*args, **kwargs)
            except TimedOutExc:
                return None
            finally:
                signal.signal(signal.SIGALRM, old)
            signal.alarm(0)
            return result

        new_f.func_name = f.func_name
        return new_f

    return decorate

如果 f 函数超时,它会抛出异常。

好吧,它可以工作,但是当我在多处理函数上使用这个装饰器并由于超时而停止时,它不会终止计算中涉及的进程。我该怎么做?

我不想启动异常并停止程序。基本上我想要的是当 f 超时时,让它返回 None 然后终止所涉及的进程。

【问题讨论】:

    标签: python timeout multiprocessing signals


    【解决方案1】:

    虽然我同意亚伦回答的要点,但我想详细说明一下。

    multiprocessing 启动的进程必须在要修饰的函数中停止;我不认为这可以简单地从装饰器本身来完成(装饰函数是唯一知道它启动了什么计算的实体)。

    除了让修饰函数捕获SIGALARM,您还可以捕获自定义TimedOutExc 异常——这可能更灵活。您的示例将变为:

    import signal
    import functools
    
    class TimedOutExc(Exception):
        """
        Raised when a timeout happens
        """
    
    def timeout(timeout):
        """
        Return a decorator that raises a TimedOutExc exception
        after timeout seconds, if the decorated function did not return.
        """
    
        def decorate(f):
    
            def handler(signum, frame):
                raise TimedOutExc()
    
            @functools.wraps(f)  # Preserves the documentation, name, etc.
            def new_f(*args, **kwargs):
    
                old_handler = signal.signal(signal.SIGALRM, handler)
                signal.alarm(timeout)
    
                result = f(*args, **kwargs)  # f() always returns, in this scheme
    
                signal.signal(signal.SIGALRM, old_handler)  # Old signal handler is restored
                signal.alarm(0)  # Alarm removed
    
                return result
    
            return new_f
    
        return decorate
    
    @timeout(10)
    def function_that_takes_a_long_time():
        try:
            # ... long, parallel calculation ...
        except TimedOutExc:
            # ... Code that shuts down the processes ...
            # ...
            return None  # Or exception raised, which means that the calculation is not complete
    

    【讨论】:

    • 看起来它可以工作。我保留我的装饰器,因为除非我总是处理函数中的异常,否则你的装饰器不会在非并行函数中返回 None 。我所做的就是像您在函数的并行计算代码部分中所做的那样处理异常,仅此而已。太感谢了。很清楚。
    • 感谢分享。保留原来的装饰器确实很有意义。
    【解决方案2】:

    我怀疑装饰器可以做到这一点:装饰器是函数的包装器;该函数是一个黑匣子。装饰器和它包装的函数之间没有通信。

    您需要做的是重写您的函数代码以使用SIGALRM 处理程序来终止它已启动的任何进程。

    【讨论】:

    • 这并不完全准确。您可以使用send 方法将值传递给生成器。如果一个函数重复产生,你可以编写一个装饰器,将它包装在一个 while 循环中,并指示函数在任何产生后终止。你实际上不必发送一个值,你可以打破循环。在大多数情况下,我不推荐这种解决方案,它与让函数跟踪自己的执行时间没有太大区别。我不确定它将如何应用于多处理,包装器必须在同一进程中运行。但这是可能的。
    猜你喜欢
    • 1970-01-01
    • 2012-01-25
    • 1970-01-01
    • 1970-01-01
    • 2022-01-09
    • 2021-07-11
    • 2016-05-31
    • 2021-10-19
    • 2021-12-05
    相关资源
    最近更新 更多