#python #python_3x #multiprocessing #python_36
Пытаюсь запустить статический метод класса через multiprocessing.Process. import multiprocessing as mp class Test: @staticmethod def method(): pass if __name__=="__main__": proc = mp.Process(target=Test.method) proc.start() Но сталкиваюсь с тем, что оно работает в питоне 3.6, но не работает в 3.4 (а для проекта требуется поддержка питона с 3.4). Полистав документацию, нашел, что можно передавать только методы, определенные в top level'е модуля. Но тогда почему это работает в питоне 3.6? Посмотрев чейнджлоги, я не нашел изменения в этой механике. Соответственно встали вопросы: Можно ли как то завернуть произвольную функцию или метод так, чтобы ее можно было передать в Process. Для отдельно взятого метода можно сделать функцию-обертку на уровне модуля: import multiprocessing as mp class Test: @staticmethod def method(): pass def method_wrapper(): # <======= This return Test.method() if __name__=="__main__": proc = mp.Process(target=method_wrapper) proc.start() но это не подойдет, когда таких функций много. Можно ли сделать какую-то функцию, которая будет принимать нужный мне метод и возвращать что-то, что можно передать в Process Соответствует ли поведение питона 3.6 тому, что в документации (или это баг/фича)? То есть могу ли я рассчитывать, что в будущем это поведение не изменится? P.S. Зачем мне это нужно? Глобально стоит задача ограничить время выполнения функций. То есть есть скрипт, который выполняет разные действия, и некоторые из них могут быть очень долгие, и нужно их прибить, если они выполняются дольше определенного времени, и сделать что-то с этим (или подождать и попробовать еще раз, или начать откат). То есть что я пытаюсь сделать: запустить функцию/метод в отдельном процессе, подождать его в главном некоторое время и убить, если надо. Потоки не подходят, я не могу их убить. Мне нужно гарантировать, что если время истекло, действие больше не выполняется, прежде чем работать дальше. В общем если есть идеи, как это сделать по другому, буду рад идеям.
Ответы
Ответ 1
Если я правильно понимаю, у Вас trace при ошибке получается примерно таким (проверял на Python 3.4.4, Windows 7 64-bit): Traceback (most recent call last): File "multiproc.py", line 17, inproc.start() File "c:\Python34\lib\multiprocessing\process.py", line 105, in start self._popen = self._Popen(self) File "c:\Python34\lib\multiprocessing\context.py", line 212, in _Popen return _default_context.get_context().Process._Popen(process_obj) File "c:\Python34\lib\multiprocessing\context.py", line 313, in _Popen return Popen(process_obj) File "c:\Python34\lib\multiprocessing\popen_spawn_win32.py", line 66, in __init__ reduction.dump(process_obj, to_child) File "c:\Python34\lib\multiprocessing\reduction.py", line 59, in dump ForkingPickler(file, protocol).dump(obj) _pickle.PicklingError: Can't pickle : attribute lookup method on __main__ failed ... File " ", line 1, in File "c:\Python34\lib\multiprocessing\spawn.py", line 100, in spawn_main new_handle = steal_handle(parent_pid, pipe_handle) File "c:\Python34\lib\multiprocessing\reduction.py", line 81, in steal_handle _winapi.PROCESS_DUP_HANDLE, False, source_pid) OSError: [WinError 87] Параметр задан неверно Обращает на себя внимание строка Can't pickle . И действительно, до версии Python 3.5 pickle не поддерживал упаковку bound-методов. Начиная с версии 3.5 поддерживает. Итого Если есть необходимость использовать именно версию 3.4, то можно воспользоваться одним из советов: Can't pickle when using multiprocessing Pool.map() или Pickling a staticmethod in Python (правда, нужно будет дополнить код обёртками). Начиная с версии 3.5 всё работает и будет работать. Варианты доработок для Python 3.4 Wrapper По совету @jfs в комментарии выше: def wrapper(klass, method): getattr(klass, method)() if __name__ == "__main__": proc = mp.Process(target=wrapper, args=(Test, "method")) proc.start() Использовать метакласс По ответу с основного Stackoverflow: http://stackoverflow.com/a/1914798/711380 class _PickleableStaticMethod(object): def __init__(self, fn, cls=None): self.cls = cls self.fn = fn def __call__(self, *args, **kwargs): return self.fn(*args, **kwargs) def __get__(self, obj, cls): return _PickleableStaticMethod(self.fn, cls) def __getstate__(self): return (self.cls, self.fn.__name__) def __setstate__(self, state): self.cls, name = state self.fn = getattr(self.cls, name).fn class pickleable_staticmethods(type): def __new__(cls, name, bases, dct): new_cls = type.__new__(cls, name, bases, dct) dct = new_cls.__dict__ for name in dct.keys(): value = new_cls.__dict__[name] if isinstance(value, staticmethod): setattr( new_cls, name, _PickleableStaticMethod(value.__get__(None, new_cls), new_cls)) return new_cls class Test(metaclass=pickleable_staticmethods): @staticmethod def method(): while True: print(time.ctime()) time.sleep(2) if __name__ == "__main__": proc = mp.Process(target=Test.method) proc.start()
Комментариев нет:
Отправить комментарий