为什么 Python 中的“pickle”和“多处理 picklability”如此不同?

Del*_*gan 5 python pickle multiprocessing

在 Windows 上使用 Pythonmultiprocessing需要将许多参数传递给子进程时“picklable”。

import multiprocessing

class Foobar:

   def __getstate__(self):
       print("I'm being pickled!")

def worker(foobar):
   print(foobar)

if __name__ == "__main__":
    # Uncomment this on Linux
    # multiprocessing.set_start_method("spawn")

    foobar = Foobar()
    process = multiprocessing.Process(target=worker, args=(foobar, ))
    process.start()
    process.join()
Run Code Online (Sandbox Code Playgroud)

文档多次明确提到这一点:

酸洗性

确保代理方法的参数是可挑选的。

[...]

继承比 pickle/unpickle 更好

当使用spawnforkserver启动方法时,许多类型需要multiprocessing可picklable,以便子进程可以使用它们。但是,通常应该避免使用管道或队列将共享对象发送到其他进程。相反,您应该安排程序,以便需要访问其他地方创建的共享资源的进程可以从祖先进程继承它。

[...]

更好的酸洗性

确保所有参数都是Process.__init__()可挑选的。另外,如果您子类化Process,请确保在Process.start调用该方法时实例将是可挑选的。

然而,我注意到“ multiprocessingpickle”和标准pickle模块之间的两个主要区别,并且我很难理解所有这些。


multiprocessing.Queue()不可“挑选”但可传递给子进程

import pickle
from multiprocessing import Queue, Process

def worker(queue):
    pass

if __name__ == "__main__":
    queue = Queue()

    # RuntimeError: Queue objects should only be shared between processes through inheritance
    pickle.dumps(queue)

    # Works fine
    process = Process(target=worker, args=(queue, ))
    process.start()
    process.join()
                                                                                                                                                                      
Run Code Online (Sandbox Code Playgroud)

如果在“ main ”中定义则不可picklable

import pickle
from multiprocessing import Process

def worker(foo):
    pass

if __name__ == "__main__":
    class Foo:
        pass

    foo = Foo()

    # Works fine
    pickle.dumps(foo)

    # AttributeError: Can't get attribute 'Foo' on <module '__mp_main__' from 'C:\\Users\\Delgan\\test.py'>
    process = Process(target=worker, args=(foo, ))
    process.start()
    process.join()
Run Code Online (Sandbox Code Playgroud)

如果multiprocessing不在pickle内部使用,那么这两种序列化对象的方式有什么本质区别呢?

另外,“继承”在多处理上下文中意味着什么?我怎么会更喜欢它而不是泡菜呢?

Dav*_*ing 7

当 amultiprocessing.Queue传递给子进程时,实际发送的是从 获取的文件描述符(或句柄)pipe,该文件描述符必须是父进程在创建子进程之前创建的。错误是为了防止尝试通过另一个(或类似的通道)pickle发送,因为它\xe2\x80\x99s太晚了,无法使用它。(Unix 系统实际上支持通过某些类型的套接字发送管道,但\xe2\x80\x99 不使用此类功能。) It\xe2\x80\x99s 预计为 \xe2\x80\x9cobvious\xe2\x80\x9d某些类型可以发送到子进程,否则这些类型将毫无用处,因此没有提及明显的矛盾。QueueQueuemultiprocessingmultiprocessing

\n\n

由于\xe2\x80\x9cspawn\xe2\x80\x9d启动方法可以\xe2\x80\x99t使用已经创建的任何Python对象来创建新进程,因此它必须重新导入主脚本以获取相关的函数/类定义。由于明显的原因,它__name__不像原始运行那样设置\xe2\x80\x99,因此任何依赖于该设置的内容都将不可用。(这里,是un(在这里,失败的

\n\n

fork方法启动子对象时,父对象\xe2\x80\x99s 对象(仅在 fork 时)仍然存在;这就是继承的意义。

\n