Декоратор тайм-аута на многопроцессорной функции - PullRequest
6 голосов
/ 23 декабря 2011

У меня есть этот декоратор, взятый непосредственно из примера, который я нашел в сети:

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, а затем завершал соответствующие процессы.

Ответы [ 2 ]

9 голосов
/ 23 декабря 2011

Хотя я согласен с основным пунктом ответа Аарона, я хотел бы остановиться подробнее.

Процессы, запущенные multiprocessing, должны быть остановлены в функции для декорирования ; Я не думаю, что это можно сделать вообще и просто из самого декоратора (украшенная функция - единственная сущность, которая знает, какие вычисления она запустила).

Вместо того, чтобы декорировать функцию catch SIGALARM, вы также можете поймать свое исключение TimedOutExc - это может быть более гибким. Ваш пример станет:

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()

        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

        new_f.func_name = f.func_name
        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
2 голосов
/ 23 декабря 2011

Я сомневаюсь, что это можно сделать с помощью декоратора: декоратор - это обертка для функции; функция черного ящика. Нет связи между декоратором и функцией, которую он оборачивает.

Что вам нужно сделать, это переписать код вашей функции, чтобы использовать обработчик SIGALRM для завершения любых запущенных процессов.

...