繁体   English   中英

当我调用multiprocessing.Process时被腌制的是什么?

[英]What is being pickled when I call multiprocessing.Process?

我知道multiprocessing使用酸洗以使进程在不同的CPU上运行,但我认为我对于被腌制的东西有点困惑。 让我们看看这段代码。

from multiprocessing import Process

def f(I):
    print('hello world!',I)

if __name__ == '__main__':
    for I in (range1, 3):
        Process(target=f,args=(I,)).start()

我假设正在被腌制的是def f(I)和参数。首先,这个假设是正确的吗?

其次,假设f(I)有一个函数调用,如:

def f(I):
    print('hello world!',I)
    randomfunction()

randomfunction的定义是否也被腌制,或者只是函数调用?

此外,如果该函数调用位于另一个文件中,该进程是否能够调用它?

在这个特定的例子中,被腌制的是平台相关的。 在支持os.fork系统上,比如Linux,这里没有任何东西被腌制。 目标函数和传递的args都由子进程通过fork继承。

在不支持fork平台上,如Windows, f函数和args元组都将被pickle并发送到子进程。 子进程将重新导入__main__模块,然后取消激活函数及其参数。

在任何一种情况下, randomfunction功能实际上都不是腌制的。 当你腌制f ,所有你真正腌制的是子函数重新构建f函数对象的指针。 这通常只是一个告诉孩子如何重新导入f的字符串:

>>> def f(I):
...     print('hello world!',I)
...     randomfunction()
... 
>>> pickle.dumps(f)
'c__main__\nf\np0\n.'

子进程将重新导入f ,然后调用它。 randomfunctionrandomfunction正确导入到原始脚本中,就可以访问randomfunction

请注意,在Python 3.4+中,您可以使用上下文在Linux上获得Windows风格的行为:

ctx = multiprocessing.get_context('spawn')
ctx.Process(target=f,args=(I,)).start()  # even on Linux, this will use pickle

上下文的描述也可能与此相关,因为它们也适用于Python 2.x:

父进程启动一个新的python解释器进程。 子进程只会继承运行进程对象run()方法所需的那些资源。 特别是,不会继承父进程中不必要的文件描述符和句柄。 与使用fork或forkserver相比,使用此方法启动进程相当慢。

可在Unix和Windows上使用。 Windows上的默认设置。

叉子

父进程使用os.fork()来分叉Python解释器。 子进程在开始时实际上与父进程相同。 父进程的所有资源都由子进程继承。 请注意,安全分叉多线程进程存在问题。

仅适用于Unix。 Unix上的默认值。

forkserver

当程序启动并选择forkserver start方法时,将启动服务器进程。 从那时起,每当需要一个新进程时,父进程就会连接到服务器并请求它分叉一个新进程。 fork服务器进程是单线程的,因此使用os.fork()是安全的。 没有不必要的资源被继承。

可在Unix平台上使用,支持通过Unix管道传递文件描述符。

请注意, forkserver仅在Python 3.4中可用,无论您使用何种平台,都无法在2.x上获得该行为。

该功能被腌制,但可能不像你想象的那样:

你可以看看这样的泡菜中究竟是什么:

pickletools.dis(pickle.dumps(f))

我明白了:

 0: c    GLOBAL     '__main__ f'
12: p    PUT        0
15: .    STOP

你会注意到那里没有任何东西对应于函数的代码。 相反,它引用了__main__ f ,它是函数的模块和名称。 因此,当它被打开时,它将始终尝试在__main__模块中查找f函数并使用它。 当您使用多处理模块时,它最终会成为与原始程序中相同功能的副本。

这确实意味着,如果你以某种方式修改哪个函数位于__main__.f你最终会取消一个不同的函数,然后你就会腌制。

多处理功能可以显示程序的完整副本以及您定义的所有功能。 所以你可以调用函数。 整个函数不会被复制,只是函数的名称。 pickle模块的假设是程序的两个副本中的函数都是相同的,因此它可以只按名称查找函数。

只有函数参数(I,)和函数f的返回值被腌制。 加载模块时,必须提供函数f的实际定义。

最简单的方法是通过代码:

from multiprocessing import Process

if __name__ == '__main__':
    def f(I):
        print('hello world!',I)

    for I in [1,2,3]:
        Process(target=f,args=(I,)).start()

返回:

AttributeError: 'module' object has no attribute 'f'

暂无
暂无

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

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