Декоратор тайм-аута на функции мультипроцессора

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

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 отключается.

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

Я не хочу запускать исключение и останавливать программу. В основном, что я хочу, это время, когда он истекает, возвратите None, а затем прекратите задействованные процессы.

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

Процессы, запущенные при 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 - 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 

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

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

Interesting Posts