簡體   English   中英

Python multiprocessing.Pool和參數pickle

[英]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.mapchunksize參數是導致混淆的原因。 顯然它會選擇為你的設置自動設置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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM