Я пытаюсь использовать multiprocessing
«s Pool.map()
функцию , чтобы разделить из работы одновременно. Когда я использую следующий код, он работает нормально:
import multiprocessing
def f(x):
return x*x
def go():
pool = multiprocessing.Pool(processes=4)
print pool.map(f, range(10))
if __name__== '__main__' :
go()
Однако, когда я использую его в более объектно-ориентированном подходе, это не работает. Это сообщение об ошибке:
PicklingError: Can't pickle <type 'instancemethod'>: attribute lookup
__builtin__.instancemethod failed
Это происходит, когда моя основная программа:
import someClass
if __name__== '__main__' :
sc = someClass.someClass()
sc.go()
и следующий мой someClass
класс:
import multiprocessing
class someClass(object):
def __init__(self):
pass
def f(self, x):
return x*x
def go(self):
pool = multiprocessing.Pool(processes=4)
print pool.map(self.f, range(10))
Кто-нибудь знает, в чем может быть проблема, или простой способ ее обойти?
python
multithreading
multiprocessing
pickle
pool
Ventolin
источник
источник
PicklingError: Can't pickle <class 'function'>: attribute lookup builtins.function failed
Ответы:
Проблема заключается в том, что многопроцессорная обработка должна выполнять сортировку между процессами, а связанные методы не могут быть выбраны. Обходной путь (считаете ли вы это «простым» или нет ;-) - это добавить инфраструктуру в вашу программу, чтобы такие методы можно было выбрать, зарегистрировав ее с помощью метода стандартной библиотеки copy_reg .
Например, вклад Стивена Бетарда в этот поток (ближе к концу потока) демонстрирует один вполне работоспособный подход, позволяющий осуществлять метод травления / расщепления через
copy_reg
.источник
_pickle_method
возвращенияself._unpickle_method
, связанный метод; поэтому, конечно, теперь pickle пытается засолить THAT - и делает это так, как вы сказали: вызывая_pickle_method
, рекурсивно. Т.е.,OO
используя код таким образом, вы неизбежно вводите бесконечную рекурсию. Я предлагаю вернуться к коду Стивена (и не поклоняться на алтаре ОО, когда это не уместно: многие вещи в Python лучше всего выполнять более функциональным способом, и это один из них).Все эти решения уродливы, потому что многопроцессорная обработка и выборка нарушены и ограничены, если вы не выйдете за пределы стандартной библиотеки.
Если вы используете вилку
multiprocessing
называетсяpathos.multiprocesssing
, вы можете напрямую использовать классы и методы класса в многопроцессорных - хmap
функциях. Это потому, чтоdill
используется вместоpickle
илиcPickle
, иdill
может сериализовать почти все в Python.pathos.multiprocessing
также предоставляет асинхронную функцию отображения ... и можетmap
функционировать с несколькими аргументами (напримерmap(math.pow, [1,2,3], [4,5,6])
)Смотрите: что могут делать мультипроцессор и укроп вместе?
и: http://matthewrocklin.com/blog/work/2013/12/05/Parallelism-and-Serialization/
И просто, чтобы быть явным, вы можете делать именно то, что вы хотели, в первую очередь, и вы можете сделать это от переводчика, если хотите.
Получить код здесь: https://github.com/uqfoundation/pathos
источник
pathos
автор. Версии, на которую вы ссылаетесь, уже несколько лет. Попробуйте версию на github, вы можете использоватьpathos.pp
или github.com/uqfoundation/ppft .pip install setuptools
, потомpip install git+https://github.com/uqfoundation/pathos.git@master
. Это получит соответствующие зависимости. Новый релиз почти готов ... теперь почти все в немpathos
также работает на Windows и3.x
совместимо.Вы также можете определить
__call__()
внутри себя методsomeClass()
, который вызывает,someClass.go()
а затем передает экземплярsomeClass()
в пул. Этот объект является маринованным, и он прекрасно работает (для меня) ...источник
__call__()
? Я думаю, что ваш ответ может быть более чистым - я изо всех сил пытаюсь понять эту ошибку, и в первый раз я прихожу, чтобы увидеть вызов. Кстати, также этот ответ поможет уточнить, что делает мультипроцессор: [ stackoverflow.com/a/20789937/305883]Некоторые ограничения для решения Стивена Бетарда:
Когда вы регистрируете свой метод класса как функцию, деструктор вашего класса неожиданно вызывается каждый раз, когда заканчивается обработка вашего метода. Поэтому, если у вас есть 1 экземпляр вашего класса, который вызывает n раз его метод, члены могут исчезнуть между двумя запусками, и вы можете получить сообщение
malloc: *** error for object 0x...: pointer being freed was not allocated
(например, открытый файл участника) илиpure virtual method called, terminate called without an active exception
(что означает, что время жизни объекта-члена, которое я использовал, было короче, чем что я думал). Я получил это при работе с n больше, чем размер пула. Вот краткий пример:Вывод:
__call__
Метод не такой эквивалент, потому что [Нет, ...] не считываются из результатов:Так что ни один из обоих методов не удовлетворяет ...
источник
None
вернетесь, потому что в вашем определении__call__
отсутствуетreturn
: так и должно бытьreturn self.process_obj(i)
.Есть еще один ярлык, который вы можете использовать, хотя он может быть неэффективным в зависимости от того, что находится в вашем классе.
Как все говорили, проблема в том, что
multiprocessing
код должен перебирать вещи, которые он отправляет запущенным подпроцессам, а средство выбора не выполняет методы экземпляра.Однако вместо отправки метода экземпляра вы можете отправить фактический экземпляр класса плюс имя вызываемой функции в обычную функцию, которая затем используется
getattr
для вызова метода экземпляра, создавая связанный метод вPool
подпроцессе. Это похоже на определение__call__
метода за исключением того, что вы можете вызывать более одной функции-члена.Кража кода @ EricH. Из его ответа и его комментирование (я набрал его заново, поэтому все имена меняются и тому подобное, по какой-то причине это казалось проще, чем вырезать и вставить :-)) для иллюстрации всей магии:
Выходные данные показывают, что действительно, конструктор вызывается один раз (в исходном pid), а деструктор вызывается 9 раз (один раз для каждой сделанной копии = 2 или 3 раза за пул-рабочий процесс, в зависимости от необходимости, плюс один раз в оригинале обработать). Это часто нормально, как в этом случае, так как сборщик по умолчанию делает копию всего экземпляра и (частично) тайно повторно заполняет его - в этом случае делает:
Вот почему, несмотря на то, что деструктор вызывается восемь раз в трех рабочих процессах, он ведет отсчет от 1 до 0 каждый раз, но, конечно, вы все равно можете столкнуться с неприятностями. При необходимости вы можете предоставить свой собственный
__setstate__
:в этом случае, например.
источник
Вы также можете определить
__call__()
внутри себя методsomeClass()
, который вызывает,someClass.go()
а затем передает экземплярsomeClass()
в пул. Этот объект является маринованным, и он прекрасно работает (для меня) ...источник
Решение от parisjohn выше прекрасно работает со мной. Плюс код выглядит чистым и легким для понимания. В моем случае есть несколько функций для вызова с помощью Pool, поэтому я изменил код parisjohn чуть ниже. Я сделал вызов, чтобы иметь возможность вызывать несколько функций, и имена функций передаются в аргументе dict из
go()
:источник
Потенциально тривиальным решением этого является переход на использование
multiprocessing.dummy
. Это основанная на потоках реализация многопроцессорного интерфейса, которая, похоже, не имеет этой проблемы в Python 2.7. У меня нет большого опыта здесь, но это быстрое изменение импорта позволило мне вызвать apply_async для метода класса.Несколько хороших ресурсов по
multiprocessing.dummy
:https://docs.python.org/2/library/multiprocessing.html#module-multiprocessing.dummy
http://chriskiehl.com/article/parallelism-in-one-line/
источник
В этом простом случае, когда
someClass.f
нет наследования каких-либо данных из класса и не прикрепления чего-либо к классу, возможное решение будет состоять в том, чтобы отделить егоf
, чтобы его можно было засолить:источник
Почему бы не использовать отдельный func?
источник
Я столкнулся с этой же проблемой, но обнаружил, что есть кодировщик JSON, который можно использовать для перемещения этих объектов между процессами.
Используйте это, чтобы создать свой список:
Затем в отображенной функции используйте это для восстановления объекта:
источник
Обновление: по состоянию на день написания, namedTuples можно выбирать (начиная с Python 2.7)
Проблема здесь в том, что дочерние процессы не могут импортировать класс объекта - в этом случае, класс P-, в случае многомодельного проекта класс P должен импортироваться везде, где используется дочерний процесс
быстрый обходной путь - сделать его импортируемым, воздействуя на глобальные переменные ()
источник