2012-02-24 8 views
37

tengo una preocupación por multiprocessing.Manager() en Python, aquí está el ejemplo,¿Cómo funciona multiprocesamiento.Manager() en python?

import multiprocessing 

def f(ns): 

    ns.x *=10 
    ns.y *= 10 

if __name__ == '__main__': 
    manager = multiprocessing.Manager() 
    ns = manager.Namespace() 
    ns.x = 1 
    ns.y = 2 

    print 'before', ns 
    p = multiprocessing.Process(target=f, args=(ns,)) 
    p.start() 
    p.join() 
    print 'after', ns 

y la salida es,

before Namespace(x=1, y=2) 
after Namespace(x=10, y=20) 

Hasta ahora, ha funcionado como se esperaba de mi, entonces yo modificado el código como este,

import multiprocessing 

def f(ns): 

    ns.x.append(10) 
    ns.y.append(10) 

if __name__ == '__main__': 
    manager = multiprocessing.Manager() 
    ns = manager.Namespace() 
    ns.x = [] 
    ns.y = [] 

    print 'before', ns 
    p = multiprocessing.Process(target=f, args=(ns,)) 
    p.start() 
    p.join() 
    print 'after', ns 

ahora, la salida es,

before Namespace(x=[], y=[]) 
after Namespace(x=[], y=[]) 

Me confundió por qué la lista no se modificó como esperaba? ¿alguien puede ayudarme a descubrir qué pasó? ¡Gracias por adelantado!

Respuesta

40

Nota: Esta respuesta puede o no ser precisa para Python 3.6+. Ver los comentarios a continuación, que parecen indicar cosas contradictorias. Actualizaré cuando tenga algo de tiempo para investigar. (Siéntase libre para editar esta respuesta si usted sabe lo que está pasando!) Objetos proxy


Manager son incapaces de propagar los cambios realizados en los objetos mutables dentro de un contenedor. En otras palabras, si tiene un objeto manager.list(), todos los cambios en la lista administrada se propagan a todos los demás procesos. Pero si tiene una lista en la lista, los cambios en la lista interna no se propagan, porque el administrador no tiene forma de detectar el cambio. Para propagar los cambios, debe modificar el objeto manager.list() directamente, como se indica en la nota here.

Por ejemplo, considere el siguiente código y su salida:

import multiprocessing 
import time 

def f(ns, ls, di): 
    ns.x += 1 
    ns.y[0] += 1 
    ns_z = ns.z 
    ns_z[0] += 1 
    ns.z = ns_z 

    ls[0] += 1 
    ls[1][0] += 1 
    ls_2 = ls[2] 
    ls_2[0] += 1 
    ls[2] = ls_2 

    di[0] += 1 
    di[1][0] += 1 
    di_2 = di[2] 
    di_2[0] += 1 
    di[2] = di_2 

if __name__ == '__main__': 
    manager = multiprocessing.Manager() 
    ns = manager.Namespace() 
    ns.x = 1 
    ns.y = [1] 
    ns.z = [1] 
    ls = manager.list([1, [1], [1]]) 
    di = manager.dict({0: 1, 1: [1], 2:[1]}) 

    print 'before', ns, ls, di 
    p = multiprocessing.Process(target=f, args=(ns, ls, di)) 
    p.start() 
    p.join() 
    print 'after', ns, ls, di 

Salida:

before Namespace(x=1, y=[1], z=[1]) [1, [1], [1]] {0: 1, 1: [1], 2: [1]} 
after Namespace(x=2, y=[1], z=[2]) [2, [1], [2]] {0: 2, 1: [1], 2: [2]} 

Como se puede ver, cuando se asigna un nuevo valor directamente al contenedor administrado, se cambia ; cuando se asigna a un contenedor mutable dentro del contenedor administrado, no cambia; pero si el contenedor mutable es entonces reasignado al contenedor administrado, cambia de nuevo.

+3

Comenzando con 3.6, los cambios en los objetos anidados se propagan automáticamente. – max

+0

He encontrado algunos problemas al usar diccionarios anidados dentro de un NameSpace con Manager en Python 3.6.4. Asegúrese de que sus objetos anidados se estén actualizando correctamente antes de seguir adelante suponiendo que lo estén. La solución para mí fue definir explícitamente cada objeto que se compartirá como un objeto Manager. – Joules

16

ns es una instancia de NamespaceProxy. Estos objetos tienen métodos especiales __getattr__, __setattr__ y __delattr__ que permiten que los valores se compartan entre los procesos. Para aprovechar este mecanismo al cambiar un valor, debe activar __setattr__.

ns.x.append(10) 

causa ns.__getattr__ a ser llamados para recuperar ns.x, pero no causa ns.__setattr__ a ser llamados.

Para solucionar esto, debe usar ns.x = ....

def f(ns): 
    tmp = ns.x  # retrieve the shared value 
    tmp.append(10) 
    ns.x = tmp  # set the shared value 
Cuestiones relacionadas