Estoy tratando de usar multiprocessing
la Pool.map()
función para dividir el trabajo simultáneamente. Cuando uso el siguiente código, funciona bien:
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()
Sin embargo, cuando lo uso en un enfoque más orientado a objetos, no funciona. El mensaje de error que da es:
PicklingError: Can't pickle <type 'instancemethod'>: attribute lookup
__builtin__.instancemethod failed
Esto ocurre cuando el siguiente es mi programa principal:
import someClass
if __name__== '__main__' :
sc = someClass.someClass()
sc.go()
y la siguiente es mi someClass
clase:
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))
¿Alguien sabe cuál podría ser el problema, o una forma fácil de solucionarlo?
python
multithreading
multiprocessing
pickle
pool
ventolin
fuente
fuente
PicklingError: Can't pickle <class 'function'>: attribute lookup builtins.function failed
Respuestas:
El problema es que el multiprocesamiento debe enredar las cosas para distribuirlas entre los procesos, y los métodos enlazados no son seleccionables. La solución (ya sea que lo consideres "fácil" o no ;-) es agregar la infraestructura a tu programa para permitir que se enreden tales métodos, registrándolos con el método de biblioteca estándar copy_reg .
Por ejemplo, la contribución de Steven Bethard a este hilo (hacia el final del hilo) muestra un enfoque perfectamente viable para permitir el método de decapado / desescamado vía
copy_reg
.fuente
_pickle_method
devolucionesself._unpickle_method
, un método encuadernado; así que, por supuesto, pepinillo ahora trata de encurtir ESO, y hace lo que le pediste: llamando_pickle_method
, recursivamente. Es decir, alOO
usar el código de esta manera, inevitablemente ha introducido una recursión infinita. Sugiero volver al código de Steven (y no adorar en el altar de OO cuando no sea apropiado: muchas cosas en Python se hacen mejor de una manera más funcional, y esta es una).Todas estas soluciones son feas porque el multiprocesamiento y el decapado están rotos y limitados a menos que salte fuera de la biblioteca estándar.
Si usa una bifurcación de
multiprocessing
llamadaspathos.multiprocesssing
, puede usar directamente clases y métodos de clase en lasmap
funciones de multiprocesamiento . Esto se debe a quedill
se usa en lugar depickle
ocPickle
, ydill
puede serializar casi cualquier cosa en Python.pathos.multiprocessing
también proporciona una función de mapa asíncrono ... y puedemap
funcionar con múltiples argumentos (por ejemplomap(math.pow, [1,2,3], [4,5,6])
)Ver: ¿Qué pueden hacer juntos el multiprocesamiento y el eneldo?
y: http://matthewrocklin.com/blog/work/2013/12/05/Parallelism-and-Serialization/
Y para ser explícito, puede hacer exactamente lo que quería hacer en primer lugar, y puede hacerlo desde el intérprete, si así lo desea.
Obtenga el código aquí: https://github.com/uqfoundation/pathos
fuente
pathos
autor La versión a la que te refieres tiene varios años. Pruebe la versión en github, puede usarpathos.pp
o github.com/uqfoundation/ppft .pip install setuptools
, entoncespip install git+https://github.com/uqfoundation/pathos.git@master
. Esto obtendrá las dependencias apropiadas. Una nueva versión está casi lista ... ahora casi todopathos
también se ejecuta en Windows, y es3.x
compatible.También puede definir un
__call__()
método dentro de susomeClass()
, que llamasomeClass.go()
y luego pasa una instanciasomeClass()
al grupo. Este objeto es pickleable y funciona bien (para mí) ...fuente
__call__()
? Creo que su respuesta podría ser la más limpia: estoy luchando por comprender este error, y la primera vez que vengo a ver la llamada. Por cierto, también esta respuesta ayuda a aclarar qué hace el multiprocesamiento: [ stackoverflow.com/a/20789937/305883]Algunas limitaciones a la solución de Steven Bethard:
Cuando registra su método de clase como una función, sorprendentemente se llama al destructor de su clase cada vez que finaliza el procesamiento de su método. Entonces, si tiene 1 instancia de su clase que llama n veces su método, los miembros pueden desaparecer entre 2 ejecuciones y puede obtener un mensaje
malloc: *** error for object 0x...: pointer being freed was not allocated
(por ejemplo, un archivo de miembro abierto) opure virtual method called, terminate called without an active exception
(lo que significa que la vida útil de un objeto miembro que usé fue más corta que lo que pensé). Obtuve esto cuando trato con n mayor que el tamaño del grupo. Aquí hay un breve ejemplo:Salida:
El
__call__
método no es tan equivalente, porque [Ninguno, ...] se lee de los resultados:Entonces ninguno de los dos métodos es satisfactorio ...
fuente
None
de vuelta porque su definición__call__
no se encuentra elreturn
: debe serreturn self.process_obj(i)
.Hay otro atajo que puede usar, aunque puede ser ineficiente dependiendo de lo que haya en las instancias de su clase.
Como todos han dicho, el problema es que el
multiprocessing
código tiene que seleccionar las cosas que envía a los subprocesos que ha iniciado, y el selector no hace métodos de instancia.Sin embargo, en lugar de enviar el método de instancia, puede enviar la instancia de clase real, más el nombre de la función a llamar, a una función ordinaria que luego usa
getattr
para llamar al método de instancia, creando así el método enlazado en elPool
subproceso. Esto es similar a definir un__call__
método, excepto que puede llamar a más de una función miembro.Robando el código de @ EricH. De su respuesta y anotándolo un poco (lo reescribí, por lo tanto, todos los cambios de nombre y demás, por alguna razón, esto parecía más fácil que cortar y pegar :-)) para ilustrar toda la magia:
El resultado muestra que, de hecho, el constructor se llama una vez (en el pid original) y el destructor se llama 9 veces (una vez por cada copia realizada = 2 o 3 veces por proceso de grupo-trabajador según sea necesario, más una vez en el original proceso). Esto a menudo está bien, como en este caso, ya que el selector predeterminado hace una copia de toda la instancia y la rellena (semi) en secreto, en este caso, haciendo:
Por eso, aunque el destructor se llama ocho veces en los tres procesos de trabajo, cuenta de 1 a 0 cada vez, pero, por supuesto, aún puede meterse en problemas de esta manera. Si es necesario, puede proporcionar su propio
__setstate__
:en este caso por ejemplo.
fuente
También puede definir un
__call__()
método dentro de susomeClass()
, que llamasomeClass.go()
y luego pasa una instanciasomeClass()
al grupo. Este objeto es pickleable y funciona bien (para mí) ...fuente
La solución de parisjohn anterior funciona bien conmigo. Además, el código se ve limpio y fácil de entender. En mi caso, hay algunas funciones para llamar usando Pool, por lo que modifiqué el código de parisjohn un poco más abajo. Hice una llamada para poder llamar a varias funciones, y los nombres de las funciones se pasan en el argumento dict de
go()
:fuente
Una solución potencialmente trivial para esto es cambiar a usar
multiprocessing.dummy
. Esta es una implementación basada en hilos de la interfaz de multiprocesamiento que no parece tener este problema en Python 2.7. No tengo mucha experiencia aquí, pero este cambio rápido de importación me permitió llamar a apply_async en un método de clase.Algunos buenos recursos sobre
multiprocessing.dummy
:https://docs.python.org/2/library/multiprocessing.html#module-multiprocessing.dummy
http://chriskiehl.com/article/parallelism-in-one-line/
fuente
En este caso simple, donde
someClass.f
no se hereda ningún dato de la clase y no se adjunta nada a la clase, una posible solución sería separarlof
, para que se pueda encurtir:fuente
¿Por qué no utilizar funciones separadas?
fuente
Me encontré con este mismo problema, pero descubrí que hay un codificador JSON que se puede usar para mover estos objetos entre procesos.
Use esto para crear su lista:
Luego, en la función asignada, use esto para recuperar el objeto:
fuente
Actualización: a partir del día de este escrito, las Tuplas nombradas son seleccionables (comenzando con Python 2.7)
El problema aquí es que los procesos secundarios no pueden importar la clase del objeto -en este caso, la clase P-, en el caso de un proyecto de modelos múltiples, la Clase P debería ser importable en cualquier lugar donde se use el proceso secundario
una solución rápida es hacer que sea importante al afectarlo a los globales ()
fuente