Python多重处理池和参数腌制

时间:2018-09-24 20:44:07

标签: python multiprocessing pickle

考虑以下示例:

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时,我希望对参数进行腌制,然后在调用作业后将其解腌(因此每次都会重新创建)。但是,观察到的输出是

[[1, 1], [1, 1], [1, 1], [1, 1], [1]]

有人可以解释发生了什么吗?我期望输出到 是五个[1]或[[1],[1、1],...,[1、1、1、1、1]的列表,但都不是这种情况。

2 个答案:

答案 0 :(得分:2)

chunksize的{​​{1}}参数是您感到困惑的原因。显然,它将选择为设置自动设置chunksize = 2,因为通过显式设置pool.map也会得到观察到的输出。

有了chunksize=2,您将得到chunksize=1 而使用[[1], [1], [1], [1], [1]],您将获得chunksize=3

如果使用打印扩展代码,则可以观察发生的情况:

[[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)

您可以看到,“追加之前”仅以空列表开始3次,而不是您期望的5倍。这是因为使用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 和五个可迭代项,您有5/2 = 2.5个任务。半任务不可能完成,因此这就是为什么您要完成3个任务的原因:2个任务带有两个项目块,而一个任务只有一个项目块。

现在,对于前两个任务,函数chunksize=2的首次执行将获得未选择的空列表并追加job。然后,第二个执行将获得与刚修改的第一个执行相同的列表,因为您的项目只是对该任务中相同列表的引用。第二次执行还更改了第一次执行的结果,因为两者都修改了相同的基础对象。在第二次执行后,任务完成,并且两次执行的结果[[1,1],[1,1]]被发送回父级。正如我们所说的,这发生在前两个任务中。

第三个任务仅执行一次1,第二个任务未修改其结果,因此结果仅为[1]。

如果在代码末尾添加job,您将看到结果中的三个独立列表具有三个不同的ID,与构建用于处理可迭代(CPython)的任务一样多。 :

for obj in out: print(id(obj))

答案 1 :(得分:0)

这将在不同数量的过程中产生不同的结果,这意味着您正在执行不安全的操作;在这种情况下,请(可能)在多个进程中对本地列表进行操作。

我不清楚您要达到的目标,但这至少表现得很一致:

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)]也会导致行为一致,尽管有所不同。