考虑以下示例:

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

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

最佳答案

chunksizepool.map参数是造成您困惑的原因。显然,它将选择为设置自动设置chunksize = 2,因为您还可以通过显式设置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)

这将为您提供以下输出:
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

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

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

第三个任务只执行一次job,结果没有被第二个修改,因此结果仅为[1]。

如果在代码末尾添加for obj in out: print(id(obj)),您将看到,结果中的三个单独列表将获得三个不同的ID,与构建用于处理可迭代(CPython)的任务一样多。
140584841382600
140584841382600
140584841383432
140584841383432
140584841383368

关于python - Python多重处理池和参数 pickle ,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/52487079/

10-09 18:50