我知道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
的定义是否也被腌制,或者只是函数调用?
此外,如果该函数调用位于另一个文件中,进程是否可以调用它?
答案 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'