Penso che questo sia dovuto a una stranezza nel modo in cui i manager vengono implementati.
Se si creano due oggetti Manager.list, e quindi aggiungere una delle liste per l'altro, il tipo di lista che si aggiunge cambiamenti all'interno della lista genitore:
>>> type(l)
<class 'multiprocessing.managers.ListProxy'>
>>> type(z)
<class 'multiprocessing.managers.ListProxy'>
>>> l.append(z)
>>> type(l[0])
<class 'list'> # Not a ListProxy anymore
l[0]
e z
non sono lo stesso oggetto, e non si comportano piuttosto il modo in cui ci si aspetta come risultato:
>>> l[0].append("hi")
>>> print(z)
[]
>>> z.append("hi again")
>>> print(l[0])
['hi again']
come si può vedere, la modifica della lista annidata non ha alcun effetto sull'oggetto ListProxy, ma cambiando la L'oggetto ListProxy cambia i nes ted list. La documentazione in realtà explicitly notes this:
Nota
Modifiche ai valori mutevoli o elementi in dict e lista proxy saranno non essere propagate tramite il gestore, in quanto la procura non ha modo di sapere quando i suoi valori o oggetti sono modificati. Per modificare tale elemento, è possibile ri-assegnare all'oggetto modificato al proxy contenitore:
Scavando attraverso il codice sorgente, si può vedere che quando si chiama append
su un ListProxy, la chiamata di aggiunta viene effettivamente inviata a un oggetto gestore tramite IPC, quindi le chiamate del manager accodano all'elenco condiviso. Ciò significa che gli argomenti di append
devono essere decapitati/non compilati. Durante il processo di unpickling, l'oggetto ListProxy viene trasformato in un normale elenco Python, che è una copia di ciò che ListProxy stava indicando (ovvero il suo referente). Questo è anche noted in the documentation:
Una caratteristica importante di oggetti proxy è che sono serializzabili così possono essere passati tra processi. Notare, tuttavia, che se un proxy viene inviato al processo del gestore corrispondente, quindi deselezionandolo, lo produce il referente stesso. Ciò significa, per esempio, che un oggetto condiviso può contenere una seconda
Quindi, tornando all'esempio precedente, se l [0] è una copia di z
, perché l'aggiornamento z
anche aggiornare l[0]
? Poiché anche la copia viene registrata con l'oggetto Proxy, quindi, quando si modifica ListProxy (z
nell'esempio sopra), vengono aggiornate anche tutte le copie registrate dell'elenco (l[0]
nell'esempio sopra riportato). Tuttavia, la copia non sa nulla del proxy, quindi quando si cambia la copia, il Proxy non cambia.
Quindi, per fare in modo che il tuo esempio funzioni, devi creare un nuovo oggetto manager.list()
ogni volta che vuoi modificare una sottolista e aggiornare solo quell'oggetto proxy direttamente, piuttosto che aggiornarlo tramite l'indice dell'elenco principale :
#!/usr/bin/python
from multiprocessing import Process, Manager
def worker(x, i, *args):
sub_l = manager.list(x[i])
sub_l.append(i)
x[i] = sub_l
if __name__ == '__main__':
manager = Manager()
x = manager.list([[]]*5)
print x
p = []
for i in range(5):
p.append(Process(target=worker, args=(x, i)))
p[i].start()
for i in range(5):
p[i].join()
print x
ecco l'output:
[email protected]:~$ ./multi_weirdness.py
[[0], [1], [2], [3], [4]]
durante il debug di esso, è possibile impostare un punto di interruzione all'interno di lavoro def (i)? Questo potrebbe non essere chiamato – linpingta
@linpingta Ho aggiunto una dichiarazione di stampa in worker (i) e il messaggio è stampato. –
Sei sicuro di 'worker' che vuoi aggiungere a' x [i] 'e non a' x' (globale)? – beroe