繁体   English   中英

当我使用 Python 3 multiprocessing.pool 时,哪些对象和变量被复制到子进程(通过酸洗)?

[英]What objects and variables are copied to child processes (by pickling) when I use Python 3 multiprocessing.pool?

在 Python 3 中创建多处理池时,我正在努力寻找有关将哪些对象和变量复制到子进程的答案。

换句话说,假设我有一个巨大的列表(~230000000 个元素)存储在一个类中,该类实现了一个使用四个子进程池的函数。 如果...,此列表是否会被复制到所有四个子进程?

  1. 子进程不从列表中读取?
  2. 子进程从列表中读取(但是,列表没有被修改)?

注意:这个答案是部分的,因为我也无法(还)找到关于这个的书面证据和文件,但如果你愿意,下面给出了一些经验数据。


以下代码用于演示如何使用Pool将数据传递/复制到子进程(实际列表l不是故意在map中使用以允许干净的打印):

from multiprocessing import Pool
import os

def process(x):
    print(os.getpid(), __name__, 'l' in globals())

# A - l = list(range(100000))
if __name__ == "__main__":
    # B - l = list(range(100000))
    with Pool() as pool:
        pool.map(process, [1,2,3,4])

    print(os.getpid(), __name__, 'l' in globals())

在 Windows 上

取消注释A时,打印输出类似于:

19604 __mp_main__ True
6392 __mp_main__ True
19604 __mp_main__ True
7048 __mp_main__ True
6568 __main__ True

将会给予。 这是因为列表是在__name__保护之外定义的,并且由于 Windows 中的进程基本上import py 文件,它们都定义了自己的l版本。

取消注释B时,打印输出类似于:

7248 __mp_main__ False
22644 __mp_main__ False
22676 __mp_main__ False
16520 __mp_main__ False
19736 __main__ True

将会给予。 即,由于列表是在__name__守卫内定义的,因此只有__main__进程定义了它,并且它通过map将参数传递给不同的进程。

在 Linux 上

取消注释任何评论将给出类似于以下内容的打印输出:

25261 __main__ True
25262 __main__ True
25263 __main__ True
25264 __main__ True
25260 __main__ True

我猜这是因为 Linux 使用fork来创建生成的进程,其中进程正在被“克隆” ,因此列表将以任何一种方式定义。

具体回答关于“spawn”使用的原始问题(正如OP所说,他们熟悉“fork”)

当创建一个进程对象时,它在main中构造,然后使用命令行 args 执行一个新的 python 进程,以共享一对用于通信的文件句柄以及一个代码存根。

该“引导”代码将尝试import主文件,这既是您需要防止导入时出现意外副作用的原因( if __name__ == "__main__": ),以及为什么该保护之外的任何内容都是“可用的”给孩子。 这主要是为了确保定义主文件中的函数,但也定义了在模块级别定义的任何变量。 这对于常量很有用,只要您有效地重新计算值并为每个进程制作一份副本并不重要。 对于大型数据集,这是非常低效的。

引导代码还将读取文件句柄之一,并尝试unpickle父进程发送给它的进程对象。 该过程的目标通常是您定义的函数,但必须注意它在导入时可以在“主”命名空间中访问(没有 lambda,没有实例方法等)。 Python 不会使用 pickle 序列化代码对象,而是中继如何正确导入函数,这对于导入时没有具体命名空间的对象会变得很冒险(侧边栏,第 3 方multiprocess库尝试通过使用dill来解决这个问题pickle一般都取得了很好的成功)。 当您对Process类进行子类化并将其他数据附加到流程实例时,也会考虑到这一点; 这一切都必须是可腌制的。

一旦子进程成功地取消了进程对象,就会调用run方法。 这通常是代码的入口点。 使用Pool ,有一个大类存在于主进程中,并使用预定义的函数启动“工作”进程,该函数接受“作业”并返回结果,直到被告知退出。 数据(由要执行的函数和该函数的参数组成的任务项)通过Queue发送到工作人员和从工作人员发送,这与发送原始Process对象的工作方式几乎相同:您放入队列的内容被腌制,发送通过文件句柄,并在孩子中未腌制。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM