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

时间:2014-09-24 20:26:51

标签: python multiprocessing pickle

我知道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的定义是否也被腌制,或者只是函数调用?

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

3 个答案:

答案 0 :(得分:7)

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

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

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

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

子进程只会重新导入f,然后调用它。只要将randomfunction正确导入到原始脚本中,就可以访问ctx = multiprocessing.get_context('spawn') ctx.Process(target=f,args=(I,)).start() # even on Linux, this will use pickle

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

forkserver

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

  

产生

     

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

     

在Unix和Windows上可用。 Windows上的默认值。

     

fork

     

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

     

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

     

forkserver

     

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

     

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

请注意,{{1}}仅适用于Python 3.4,无论您使用何种平台,都无法在2.x上获得该行为。

答案 1 :(得分:2)

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

你可以看看这样的泡菜实际上是什么:

pickletools.dis(pickle.dumps(f))

我明白了:

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

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

这确实意味着如果你以某种方式修改了__main__.f中的哪个函数,那么你最终会取消对其他函数的修改,然后你就会腌制。

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

答案 2 :(得分:0)

仅腌制函数参数(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'