![](/img/trans.png)
[英]What objects and variables are copied to child processes (by pickling) when I use Python 3 multiprocessing.pool?
[英]Python multiprocessing.Pool and argument pickling
請考慮以下示例:
import multiprocessing as mp
def job(l):
l.append(1)
return l
if __name__ == "__main__":
pool = mp.Pool(1)
my_list = []
out = pool.map(job, [my_list for i in range(5)])
pool.close()
pool.join()
print(out)
在調用pool.map時,我希望在調用作業后對參數進行pickle然后取消激活(因此每次都會重新創建)。 但是,觀察到的輸出是
[[1, 1], [1, 1], [1, 1], [1, 1], [1]]
有人可以解釋一下發生了什么嗎? 我期望輸出是五[1]或[[1],[1,1],......,[1,1,1,1,1]]的列表,兩者都不是這種情況。
pool.map
的chunksize
參數是導致混淆的原因。 顯然它會選擇為你的設置自動設置chunksize = 2,因為你也可以通過顯式設置chunksize=2
獲得你觀察到的輸出。
使用chunksize=1
你會得到[[1], [1], [1], [1], [1]]
和chunksize=3
你會得到[[1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1], [1, 1]]
chunksize=3
[[1], [1], [1], [1], [1]]
[[1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1], [1, 1]]
。
如果您使用打印擴展代碼,您可以觀察會發生什么:
import multiprocessing as mp
def job(l):
print(f'before append {l}')
l.append(1)
print(f'after append {l}')
return l
if __name__ == "__main__":
pool = mp.Pool(1)
my_list = []
out = pool.map(job, [my_list for _ in range(5)], chunksize=2)
pool.close()
pool.join()
print(out)
這將給你這個輸出:
before append []
after append [1]
before append [1]
after append [1, 1]
before append []
after append [1]
before append [1]
after append [1, 1]
before append []
after append [1]
[[1, 1], [1, 1], [1, 1], [1, 1], [1]]
Process finished with exit code 0
你可以看到,“追加前”只用空列表開始三次,而不是你期望的五倍。 那是因為chunksize=2
和可迭代中的五個項目你有5/2 = 2.5個任務。 半個任務是不可能的,這就是為什么你最終得到3個任務:2個具有兩個項目塊的任務和一個具有一個項目塊的任務。
現在,對於前兩個任務,第一次執行函數job
將獲得未標記的空列表並附加1
。 然后第二次執行獲得剛剛修改的第一次執行的相同列表,因為您的項目只是對此任務中相同列表的引用。 第二次執行也會更改第一次執行的結果,因為它們都修改了相同的底層對象。 第二次執行后,任務完成,兩次執行[[1,1],[1,1]]的結果將被發送回父級。 正如我們所說,這發生在前兩個任務中。
第三個任務只有一個job
執行,它的結果不會被第二個修改,所以結果只有[1]。
如果你for obj in out: print(id(obj))
代碼的末尾添加for obj in out: print(id(obj))
,你會看到,你得到三個不同的id用於結果中的三個單獨的列表,就像為了處理你的iterable而構建的任務一樣多(CPython的):
140584841382600
140584841382600
140584841383432
140584841383432
140584841383368
這會產生不同數量的過程產生不同的結果,這意味着您正在做一些不是過程安全的事情; 在這種情況下,在(可能)多個進程中的本機列表上操作。
我對你想要達到的目標並不十分清楚,但這至少表現得很好:
from multiprocessing import Pool, Manager
def job(l):
l.append(1)
return l
if __name__ == "__main__":
manager = Manager()
for proc_count in range(1, 6):
print(proc_count)
pool = Pool(proc_count)
my_list = manager.list()
out = pool.map(job, [my_list for i in range(5)])
pool.close()
pool.join()
print(list(list(o) for o in out))
如果那不是你想要的,忘記管理員,刪除my_list
並[list() for i in range(5)]
使用[list() for i in range(5)]
也會導致一致但不同的行為。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.