J'ai une préoccupation concernant le multiprocessing.Manager () en python, voici l'exemple,
import multiprocessing
def f(ns):
ns.x *=10
ns.y *= 10
if __== '__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
et la sortie est,
before Namespace(x=1, y=2)
after Namespace(x=10, y=20)
Jusqu'à présent, cela fonctionnait comme prévu, puis j'ai modifié le code comme ceci,
import multiprocessing
def f(ns):
ns.x.append(10)
ns.y.append(10)
if __== '__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
maintenant, la sortie est,
before Namespace(x=[], y=[])
after Namespace(x=[], y=[])
Cela m'a dérouté pourquoi la liste n'a pas été modifiée comme prévu? quelqu'un peut-il m'aider à comprendre ce qui s'est passé? Merci d'avance!
Les objets proxy du gestionnaire ne peuvent pas propager les modifications apportées aux objets mutables (non gérés) à l'intérieur d'un conteneur. En d'autres termes, si vous avez un objet manager.list()
, toutes les modifications apportées à la liste gérée elle-même sont propagées à tous les autres processus. Mais si vous avez une liste normale Python à l'intérieur de cette liste, aucune modification de la liste intérieure ne se propage, car le gestionnaire n'a aucun moyen de détecter le changement.
Afin de propager les modifications, vous devez également utiliser les objets manager.list()
pour les listes imbriquées (nécessite Python 3.6 ou plus récent ), ou vous devez modifier la fonction manager.list()
objet directement (voir la note sur manager.list
en Python 3.5 ou plus ancien ).
Par exemple, considérez le code suivant et sa sortie:
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 # unmanaged, not assigned back
ls_2 = ls[2] # unmanaged...
ls_2[0] += 1
ls[2] = ls_2 # ... but assigned back
ls[3][0] += 1 # managed, direct manipulation
di[0] += 1
di[1][0] += 1 # unmanaged, not assigned back
di_2 = di[2] # unmanaged...
di_2[0] += 1
di[2] = di_2 # ... but assigned back
di[3][0] += 1 # managed, direct manipulation
if __== '__main__':
manager = multiprocessing.Manager()
ns = manager.Namespace()
ns.x = 1
ns.y = [1]
ns.z = [1]
ls = manager.list([1, [1], [1], manager.list([1])])
di = manager.dict({0: 1, 1: [1], 2: [1], 3: manager.list([1])})
print('before', ns, ls, ls[2], di, di[2], sep='\n')
p = multiprocessing.Process(target=f, args=(ns, ls, di))
p.start()
p.join()
print('after', ns, ls, ls[2], di, di[2], sep='\n')
Sortie:
before
Namespace(x=1, y=[1], z=[1])
[1, [1], [1], <ListProxy object, typeid 'list' at 0x10b8c4630>]
[1]
{0: 1, 1: [1], 2: [1], 3: <ListProxy object, typeid 'list' at 0x10b8c4978>}
[1]
after
Namespace(x=2, y=[1], z=[2])
[2, [1], [2], <ListProxy object, typeid 'list' at 0x10b8c4630>]
[2]
{0: 2, 1: [1], 2: [2], 3: <ListProxy object, typeid 'list' at 0x10b8c4978>}
[2]
Comme vous pouvez le voir, lorsqu'une nouvelle valeur est affectée directement au conteneur géré, elle change; lorsqu'il est affecté à un conteneur modifiable dans le conteneur géré, il ne change pas; mais si le conteneur modifiable est ensuite réaffecté au conteneur géré, il change à nouveau. L'utilisation d'un conteneur géré imbriqué fonctionne également, détectant directement les modifications sans avoir à les affecter de nouveau au conteneur parent.
ns
est une instance de NamespaceProxy. Ces objets ont des __getattr__
, __setattr__
, et __delattr__
méthodes qui permettent de partager des valeurs entre les processus. Afin de profiter de ce mécanisme lors de la modification d'une valeur, vous devez déclencher __setattr__
.
ns.x.append(10)
provoque ns.__getattr__
à appeler pour récupérer ns.x
, mais cela ne provoque pas ns.__setattr__
être appelé.
Pour résoudre ce problème, vous devez utiliser ns.x = ...
.
def f(ns):
tmp = ns.x # retrieve the shared value
tmp.append(10)
ns.x = tmp # set the shared value