none how dict python multiprocessing

how - Administrador de Python dict en multiprocesamiento



py dict get (4)

Aquí hay un código simple de multiprocesamiento:

from multiprocessing import Process, Manager manager = Manager() d = manager.dict() def f(): d[1].append(4) print d if __name__ == ''__main__'': d[1] = [] p = Process(target=f) p.start() p.join()

La salida que obtengo es:

{1: []}

¿Por qué no obtengo {1: [4]} como salida?


Aquí está lo que escribiste:

# from here code executes in main process and all child processes # every process makes all these imports from multiprocessing import Process, Manager # every process creates own ''manager'' and ''d'' manager = Manager() # BTW, Manager is also child process, and # in its initialization it creates new Manager, and new Manager # creates new and new and new # Did you checked how many python processes were in your system? - a lot! d = manager.dict() def f(): # ''d'' - is that ''d'', that is defined in globals in this, current process d[1].append(4) print d if __name__ == ''__main__'': # from here code executes ONLY in main process d[1] = [] p = Process(target=f) p.start() p.join()

Aquí está lo que deberías haber escrito:

from multiprocessing import Process, Manager def f(d): d[1] = d[1] + [4] print d if __name__ == ''__main__'': manager = Manager() # create only 1 mgr d = manager.dict() # create only 1 dict d[1] = [] p = Process(target=f,args=(d,)) # say to ''f'', in which ''d'' it should append p.start() p.join()


Creo que esto es un error en las llamadas de proxy de administrador. Puede evitar evitando los métodos de llamada de la lista compartida, como:

from multiprocessing import Process, Manager manager = Manager() d = manager.dict() def f(): # get the shared list shared_list = d[1] shared_list.append(4) # forces the shared list to # be serialized back to manager d[1] = shared_list print d if __name__ == ''__main__'': d[1] = [] p = Process(target=f) p.start() p.join() print d


La razón por la que el nuevo elemento adjunto a d[1] no se imprime se indica en la documentación oficial de Python :

Las modificaciones a los valores o elementos mutables en los proxies dict y list no se propagarán a través del administrador, ya que el proxy no tiene forma de saber cuándo se modifican sus valores o elementos. Para modificar un elemento de este tipo, puede volver a asignar el objeto modificado al proxy de contenedor.

Por lo tanto, esto es en realidad lo que sucede:

from multiprocessing import Process, Manager manager = Manager() d = manager.dict() def f(): # invoke d.__getitem__(), returning a local copy of the empty list assigned by the main process, # (consider that a KeyError exception wasn''t raised, so a list was definitely returned), # and append 4 to it, however this change is not propagated through the manager, # as it''s performed on an ordinary list with which the manager has no interaction d[1].append(4) # convert d to string via d.__str__() (see https://docs.python.org/2/reference/datamodel.html#object.__str__), # returning the "remote" string representation of the object (see https://docs.python.org/2/library/multiprocessing.html#multiprocessing.managers.SyncManager.list), # to which the change above was not propagated print d if __name__ == ''__main__'': # invoke d.__setitem__(), propagating this assignment (mapping 1 to an empty list) through the manager d[1] = [] p = Process(target=f) p.start() p.join()

Al reasignar d[1] con una nueva lista, o incluso con la misma lista una vez más, después de que se actualice, se activa el administrador para propagar el cambio:

from multiprocessing import Process, Manager manager = Manager() d = manager.dict() def f(): # perform the exact same steps, as explained in the comments to the previous code snippet above, # but in addition, invoke d.__setitem__() with the changed item in order to propagate the change l = d[1] l.append(4) d[1] = l print d if __name__ == ''__main__'': d[1] = [] p = Process(target=f) p.start() p.join()

La línea d[1] += [4] también habría funcionado.

Alternativamente, desde Python 3.6 , por este conjunto de cambios que sigue a este problema , también es posible usar objetos proxy anidados que propagan automáticamente cualquier cambio realizado en ellos al objeto proxy que lo contiene. Por lo tanto, reemplazar la línea d[1] = [] con d[1] = manager.list() también corrige el problema:

from multiprocessing import Process, Manager manager = Manager() d = manager.dict() def f(): d[1].append(4) # the __str__() method of a dict object invokes __repr__() on each of its items, # so explicitly invoking __str__() is required in order to print the actual list items print({k: str(v) for k, v in d.items()} if __name__ == ''__main__'': d[1] = manager.list() p = Process(target=f) p.start() p.join()

Desafortunadamente, esta corrección de errores no se realizó en Python 2.7 (a partir de Python 2.7.13 ).

NOTA (ejecutándose bajo el sistema operativo Windows ):

Aunque el comportamiento descrito se aplica también al sistema operativo Windows , los fragmentos de código adjuntos fallarán cuando se ejecuten en Windows debido al mecanismo de creación de procesos diferente , que se basa en la API CreateProcess() lugar de la llamada del sistema fork() , que no es No es compatible.

Cuando se crea un nuevo proceso a través del módulo de multiprocesamiento , Windows crea un nuevo proceso de interpretación de Python que importa el módulo principal, con efectos secundarios potencialmente peligrosos. Para evitar este problema, se recommended la siguiente guía de programación:

Asegúrese de que el módulo principal pueda ser importado de manera segura por un nuevo intérprete de Python sin causar efectos secundarios no deseados (como comenzar un nuevo proceso).

Por lo tanto, ejecutar los fragmentos de código adjuntos como en Windows intentaría crear un número infinito de procesos debido a la línea manager = Manager() . Esto se puede solucionar fácilmente creando los objetos Manager y Manager.dict dentro de la cláusula if __name__ == ''__main__'' y pasando el objeto Manager.dict como un argumento a f() , como se hace en esta respuesta .

Más detalles sobre el tema se pueden encontrar en esta respuesta .


from multiprocessing import Process, Manager manager = Manager() d = manager.dict() l=manager.list() def f(): l.append(4) d[1]=l print d if __name__ == ''__main__'': d[1]=[] p = Process(target=f) p.start() p.join()