我正在使用Pythonmultiprocessing
创建一个并行应用程序。流程需要共享一些数据,对此我使用Manager
。但是,我有一些通用的功能,这些功能需要进程调用以及需要访问该Manager
对象存储的数据。我的问题是,是否可以避免将Manager
实例作为参数传递给这些通用函数,而是像全局变量一样使用它。换句话说,请考虑以下代码:
import multiprocessing as mp
manager = mp.Manager()
global_dict = manager.dict(a=[0])
def add():
global_dict['a'] += [global_dict['a'][-1]+1]
def foo_parallel(var):
add()
print var
num_processes = 5
p = []
for i in range(num_processes):
p.append(mp.Process(target=foo_parallel,args=(global_dict,)))
[pi.start() for pi in p]
[pi.join() for pi in p]
运行正常,并p=[0,1,2,3,4,5]
在我的机器上返回。但是,这是“好形式”吗?这是执行此操作的好方法,就像定义add(var)
和调用一样add(var)
好吗?
您的代码示例似乎比表格具有更大的问题。您只有靠运气才能获得所需的输出。重复执行将产生不同的结果。那是因为这+=
不是原子操作。在任何一个进程更新之前,多个进程可以一个接一个地读取相同的旧值,并且它们将回写相同的值。为了防止这种行为,您必须Manager.Lock
另外使用。
对于您最初关于“良好形式”的问题。
IMO将子进程的main函数显式foo_parallel
传递global_dict
给泛型函数会更干净add(var)
。那将是依赖注入的一种形式,并具有一些优点。在您的示例中,并非十分详尽:
允许隔离测试
提高代码可重用性
更容易的调试(检测被管理对象的不可访问性不应延迟,直到
add
被调用为止(快速失败)更少的样板代码(例如,需要多个功能的资源上的try-excepts块)
作为旁注。仅将列表理解用于其副作用被认为是“代码异味”。如果不需要结果列表,则使用for循环。
码:
import os
from multiprocessing import Process, Manager
def add(l):
l += [l[-1] + 1]
return l
def foo_parallel(global_dict, lock):
with lock:
l = global_dict['a']
global_dict['a'] = add(l)
print(os.getpid(), global_dict)
if __name__ == '__main__':
N_WORKERS = 5
with Manager() as manager:
lock = manager.Lock()
global_dict = manager.dict(a=[0])
pool = [Process(target=foo_parallel, args=(global_dict, lock))
for _ in range(N_WORKERS)]
for p in pool:
p.start()
for p in pool:
p.join()
print('result', global_dict)
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句