为什么在Python中“挑剔”和“多处理可控性”如此不同?

时间:2019-07-06 09:33:13

标签: python multiprocessing pickle

在Windows上使用Python的multiprocessing时,将许多参数传递给子进程时,它们必须是“可拾取的”。

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()

文档mentions this explicitly多次:

  

可剥性

     

确保代理方法的参数可腌。

     

[...]

     

比泡菜/腌制更好的继承

     

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

     

[...]

     

更具可选择性

     

确保Process.__init__()的所有自变量都是可选取的。另外,如果您继承Process的子类,请确保在调用Process.start方法时实例是可腌制的。

但是,我注意到“ multiprocessing泡菜”和标准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()

如果在“ 主要”中定义,则不可腌制

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()

如果multiprocessing在内部未使用pickle,那么这两种对象序列化方式之间的固有区别是什么?

此外,在多处理上下文中“继承”是什么意思?我应该怎么比泡菜更喜欢?

1 个答案:

答案 0 :(得分:1)

multiprocessing.Queue传递给子进程时,实际发送的是从pipe获得的文件描述符(或句柄),该文件描述符必须由父级在创建子级之前创建。来自pickle的错误是为了防止尝试通过另一个Queue(或类似渠道)发送Queue,因为那时使用它为时已晚。 (Unix系统实际上确实支持通过某些类型的套接字发送管道,但是multiprocessing不使用此类功能。)可以肯定的是,某些multiprocessing类型可以发送给子进程。否则将是无用的,因此没有提及明显的矛盾。

由于“ spawn”启动方法无法使用已创建的任何Python对象创建新进程,因此必须重新导入主脚本以获得相关的函数/类定义。由于明显的原因,它没有将__name__设置为原始运行,因此任何依赖于该设置的内容将不可用。 (在这里, un 酸洗失败了,这就是您手动酸洗起作用的原因。)

fork 方法从子对象开始,但其父对象(仅在分叉时)仍然存在;这就是继承的意思。