2010-09-08 26 views
26

Tengo un objeto Python bastante complejo que necesito compartir entre múltiples procesos. Lanzo estos procesos usando multiprocessing.Process. Cuando comparto un objeto con multiprocessing.Queue y multiprocessing.Pipe en él, se comparten muy bien. Pero cuando trato de compartir un objeto con otros objetos que no son de multiprocesamiento, parece que Python los bifurca. ¿Es eso cierto?¿Compartir un objeto complejo entre procesos de Python?

Intenté usar multiprocesamiento.Valor. Pero no estoy seguro de qué tipo debe ser? Mi clase de objeto se llama MyClass. Pero cuando intento multiprocess.Value(MyClass, instance), falla con:

TypeError: this type has no size

Alguna idea de lo que está pasando?

+1

relacionado: http://stackoverflow.com/questions/659865/python-multiprocessing-sharing-a-large-read-only-object-between-processes – tokland

Respuesta

23

Puede hacer esto usando las clases de "multiprocesamiento" Python de "Administrador" y una clase de proxy que defina. Desde los documentos de Python: http://docs.python.org/library/multiprocessing.html#proxy-objects

Lo que quiere hacer es definir una clase de proxy para su objeto personalizado y luego compartir el objeto usando un "Administrador remoto": mire los ejemplos en la misma página de documento vinculada para "administrador remoto" donde los documentos muestran cómo compartir una cola remota. Harás lo mismo, pero tu llamada a your_manager_instance.register() incluirá tu clase de proxy personalizada en su lista de argumentos.

De esta manera, está configurando un servidor para compartir el objeto personalizado con un proxy personalizado. Sus clientes necesitan acceso al servidor (una vez más, consulte los excelentes ejemplos de documentación sobre cómo configurar el acceso de cliente/servidor a una cola remota, pero en lugar de compartir una cola, está compartiendo el acceso a su clase específica).

+3

El código de esta pregunta me ayudó a completar la página del documento. Es un ejemplo con una clase personalizada. http://stackoverflow.com/questions/11951750/sharing-object-class-instance-in-python-using-managers – EarlCrapstone

15

Después de mucha investigación y pruebas, encontré que el "Administrador" hace este trabajo en un nivel de objeto no complejo.

El código siguiente muestra que el objeto inst se comparte entre procesos, lo que significa que la propiedad var de inst se cambia al exterior cuando el proceso hijo lo cambia.

from multiprocessing import Process, Manager 
from multiprocessing.managers import BaseManager 

class SimpleClass(object): 
    def __init__(self): 
     self.var = 0 

    def set(self, value): 
     self.var = value 

    def get(self): 
     return self.var 


def change_obj_value(obj): 
    obj.set(100) 


if __name__ == '__main__': 
    BaseManager.register('SimpleClass', SimpleClass) 
    manager = BaseManager() 
    manager.start() 
    inst = manager.SimpleClass() 

    p = Process(target=change_obj_value, args=[inst]) 
    p.start() 
    p.join() 

    print inst     # <__main__.SimpleClass object at 0x10cf82350> 
    print inst.get()    # 100 

bien, el código de seguridad es lo suficientemente si sólo necesita compartir objetos simples.

¿Por qué no hay complejos? Debido puede fallar si el objeto está anidado (objeto dentro de objeto):

from multiprocessing import Process, Manager 
from multiprocessing.managers import BaseManager 

class GetSetter(object): 
    def __init__(self): 
     self.var = None 

    def set(self, value): 
     self.var = value 

    def get(self): 
     return self.var 


class ChildClass(GetSetter): 
    pass 

class ParentClass(GetSetter): 
    def __init__(self): 
     self.child = ChildClass() 
     GetSetter.__init__(self) 

    def getChild(self): 
     return self.child 


def change_obj_value(obj): 
    obj.set(100) 
    obj.getChild().set(100) 


if __name__ == '__main__': 
    BaseManager.register('ParentClass', ParentClass) 
    manager = BaseManager() 
    manager.start() 
    inst2 = manager.ParentClass() 

    p2 = Process(target=change_obj_value, args=[inst2]) 
    p2.start() 
    p2.join() 

    print inst2     # <__main__.ParentClass object at 0x10cf82350> 
    print inst2.getChild()   # <__main__.ChildClass object at 0x10cf6dc50> 
    print inst2.get()    # 100 
    #good! 

    print inst2.getChild().get() # None 
    #bad! you need to register child class too but there's almost no way to do it 
    #even if you did register child class, you may get PicklingError :) 

Creo que la razón principal de este comportamiento se debe a que Manager es sólo un candybar construir en la parte superior de las herramientas de comunicación de bajo nivel como tubería /cola.

Por lo tanto, este enfoque es no bien recomendado para el caso de multiprocesamiento. Siempre es mejor si puede usar herramientas de bajo nivel como lock/semáforo/pipe/queue o herramientas de alto nivel como Redis queue o Redis publicar/suscribir para casos de uso complicado (solo mi recomendación jaja).

+0

¿Cómo compartir un objeto complejo? –

0

Para ahorrar algunos dolores de cabeza con los recursos compartidos, puede tratar de recopilar datos que necesitan acceso a un recurso singleton en una declaración de devolución de la función que está mapeada, p.pool.imap_unordered y luego procesar aún más en un bucle que recupera los resultados parciales:

for result in in pool.imap_unordered(process_function, iterable_data): 
    do_something(result) 

Si no hay muchos datos que obtiene devueltos, entonces puede que no haya tanta sobrecarga en hacer esto.

2

he aquí un paquete de Python que hice solo para eso (compartir objetos complejos entre procesos).

git: https://github.com/dRoje/pipe-proxy

La idea es crear un proxy para el objeto y pasarlo a un proceso. Luego usa el proxy como si tuviera una referencia al objeto original. Aunque solo puede usar llamadas a métodos, de modo que al acceder a las variables del objeto se hace lanzar setters y getters.

Digamos que tenemos un objeto llamado ‘ejemplo’, la creación de proxy y escucha de proxy es fácil:

from pipeproxy import proxy 
example = Example() 
exampleProxy, exampleProxyListener = proxy.createProxy(example) 

Ahora se envían al proxy a otro proceso.

p = Process(target=someMethod, args=(exampleProxy,)) p.start() 

utilizarlo en el otro proceso de igual modo que el objeto original (ejemplo):

def someMethod(exampleProxy): 
    ... 
    exampleProxy.originalExampleMethod() 
    ... 

Pero usted tiene que escuchar en el proceso principal:

exampleProxyListener.listen() 

Lea más y encuentre ejemplos aquí:

http://matkodjipalo.com/index.php/2017/11/12/proxy-solution-python-multiprocessing/

Cuestiones relacionadas