[英]Python (multiprocessing): How to pass a dictionary as the argument of a worker process initializer function?
[英]Python multiprocessing problem with dictionary as argument
我有一個函數通過多處理池並行化另一個函數,該池將字典作為輸入。 我希望下面的代碼只打印從 0 到 32 的數字。但是,結果顯示有很多數字被打印了不止一次。
有人有想法嗎?
import multiprocessing as mp
import numpy as np
import functools
def test(name, t_dict):
t_dict['a'] = name
return t_dict
def mp_func(func, iterator ,**kwargs):
f_args = functools.partial(func, **kwargs)
pool = mp.Pool(mp.cpu_count())
res = pool.map(f_args, iterator)
pool.close()
return res
mod =dict()
m =33
res = mp_func(func=test, iterator=np.arange(m), t_dict=mod)
for di in res:
print(di['a'])
問題是t_dict
作為偏函數f_args
的一部分f_args
。 偏函數是<class 'functools.partial'>
實例。 創建部分時,它會獲得對test
的引用和mod
的空字典。 每次調用f_args
,都會修改部分對象上的那個字典。 在單個過程中使用列表更容易發現這一點。
>>> def foo(name, t_list):
... t_list.append(name)
... return t_list
...
>>> mod = []
>>> f = functools.partial(foo, t_list=mod)
>>> f(0)
[0]
>>> f(1)
[0, 1]
>>> f(2)
[0, 1, 2]
>>> mod
[0, 1, 2]
當您pool.map(f_args, iterator)
, f_args
被腌制並發送到每個子f_args
成為工作人員。 因此,每個子進程都有一個唯一的字典副本,該副本將針對子進程碰巧獲得的每個迭代值進行更新。
為了效率,多處理將分塊數據。 也就是說,每個子進程都收到一個迭代值列表,它將處理成一個響應列表,以作為一個組返回。 但是由於每個響應都引用相同的單個 dict,當塊返回給父級時,所有響應都只保存最終值集。 如果處理了0, 1, 2
,則返回2, 2, 2
。
解決方案將取決於您的數據。 在池進程和父進程之間來回傳遞數據的成本很高,因此理想情況下,數據完全在工作進程中生成。 在這種情況下,放棄partial
並讓工作人員創建字典。
很可能你的情況比這更復雜。
import multiprocessing as mp
import numpy as np
def test(name):
retrurn ('a':name}
def mp_func(func, iterator ,**kwargs):
pool = mp.Pool(mp.cpu_count())
res = pool.map(test, iterator)
pool.close()
return res
m =33
res = mp_func(func=test, iterator=np.arange(m))
for di in res:
print(di['a'])
正如每個人都告訴您的那樣,一般來說,讓多個線程/進程都修改同一個位置,然后期望該位置具有您的線程賦予它的值是一個壞主意。
如果共享數據結構的所有變異只發生在一處,您的代碼將運行得更好。 所以總體方案是:
def worker(key):
... calculate value produced by key ...
return key, value
def runner():
with mp.Pool() as pool:
for key, value in pool.imap_unordered(worker, np.arange(m), chunksize=...):
... do fast mutation here ...
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.