[英]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.