[英]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
,然后调用它。 randomfunction
将randomfunction
正确导入到原始脚本中,就可以访问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.