Класс timeout decorator с многопроцессорной обработкой выдает ошибку маринования


Так что на windows signal и thread approachc вообще плохие идеи / не работают для таймаута функций.

Я сделал следующий код таймаута, который выбрасывает timeout exception из multiprocessing, когда код занял много времени. Это именно то, чего я хочу.

 def timeout(timeout, func, *arg):
    with Pool(processes=1) as pool:
        result = pool.apply_async(func, (*arg,))
        return result.get(timeout=timeout)

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

class TimeWrapper(object):

    def __init__(self, timeout=10):
        """Timing decorator"""
        self.timeout = timeout

    def __call__(self, f):
        def wrapped_f(*args):
            with Pool(processes=1) as pool:
                result = pool.apply_async(f, (*args,))
                return result.get(timeout=self.timeout)

        return wrapped_f

Это дает ошибку маринования:

@TimeWrapper(7)
def func2(x, y):
    time.sleep(5)
    return x*y

File "C:\Users\rmenk\AppData\Local\Continuum\anaconda3\lib\multiprocessing\reduction.py", line 51, in dumps cls(buf, protocol).dump(obj) _pickle.PicklingError: Can't pickle <function func2 at 0x000000770C8E4730>: it's not the same object as __main__.func2

Я подозреваю, что это связано с тем, что мультипроцессор и декоратор не играют хорошо, но я на самом деле не знаю, как заставить их играть хорошо. Есть идеи, как это исправить?

PS: я провел некоторые обширные исследования на этом сайте и в других местах, но не нашел никаких ответов, которые работают, будь то с pebble, threading, как декоратор функций или иначе. Если у вас есть решение, которое, как вы знаете, работает на windows и python 3.5 я был бы очень рад просто использовать это.

1   2   2018-04-20 10:53:55

1 ответ:

То, что вы пытаетесь достичь, особенно громоздко в Windows. Основная проблема заключается в том, что, когда вы украшаете функцию, вы затеняете ее. Это происходит, чтобы работать просто отлично в UNIX из-за того, что он использует fork стратегия создания нового процесса.

Однако в Windows новый процесс будет пустым, когда запускается новый интерпретатор Python и загружает ваш модуль. Когда модуль загружается, декоратор скрывает реальную функцию, что затрудняет ее поиск. Протокол pickle.

Единственный способ сделать это правильно-полагаться на функцию батута, которая будет установлена во время украшения. Вы можете посмотреть, как это делается на pebble но, пока вы не делаете это для упражнения, я бы рекомендовал использовать pebble непосредственно, поскольку он уже предлагает то, что вы ищете.
from pebble import concurrent

@concurrent.process(timeout=60)
def my_function(var, keyvar=0):
    return var + keyvar

future = my_function(1, keyvar=2)
future.result()