Python多处理和管理器

时间:2018-09-21 01:59:51

标签: python multithreading parallel-processing multiprocessing python-multiprocessing

我正在使用Python的multiprocessing创建并行应用程序。流程需要共享一些数据,对此我使用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)一样好吗?

1 个答案:

答案 0 :(得分:1)

您的代码示例似乎比表格具有更大的问题。您只有靠运气才能获得所需的输出。重复执行将产生不同的结果。这是因为+=不是原子操作。在任何一个进程更新之前,多个进程可以一次又一次读取同一旧值,并且它们将回写相同的值。为了防止这种行为,您必须另外使用Manager.Lock


对于您最初有关“良好形式”的问题。

IMO,让子进程foo_parallel的主要功能显式传递global_dict到泛型函数add(var)中会更干净。那将是dependency injection的一种形式,并具有一些优点。在您的示例中,并非详尽无遗:

  
      
  • 允许隔离测试
  •   
  • 提高代码的可重用性
  •   
  • 更方便的调试(检测被管理对象的不可访问性不应延迟,直到调用addfail fast

  •   
  • 更少的样板代码(例如,需要多个功能的资源上的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)