多处理忽略“__setstate__”

multiprocessing ignores "__setstate__"

我假设 multiprocessing 包使用 pickle 在进程之间发送东西。但是pickle关注对象的__getstate____setstate__方法。多处理似乎忽略了它们。这个对吗?我糊涂了吗?

要复制,请安装 docker,然后在命令行中输入

$ docker run python:3.4 python -c "import pickle
import multiprocessing
import os

class Tricky:
    def __init__(self,x):
        self.data=x

    def __setstate__(self,d):
        self.data=10

    def __getstate__(self):
        return {}

def report(ar,q):
    print('running report in pid %d, hailing from %d'%(os.getpid(),os.getppid()))
    q.put(ar.data)

print('module loaded in pid %d, hailing from pid %d'%(os.getpid(),os.getppid()))
if __name__ == '__main__':
    print('hello from pid %d'%os.getpid())
    ar = Tricky(5)
    q = multiprocessing.Queue()
    p = multiprocessing.Process(target=report, args=(ar, q))
    p.start()
    p.join()
    print(q.get())
    print(pickle.loads(pickle.dumps(ar)).data)"

你应该得到类似

的东西
module loaded in pid 1, hailing from pid 0
hello from pid 1
running report in pid 5, hailing from 1
5
10

我本以为会是“10”“10”,但实际上是“5”“10”。这意味着什么?

(注意:根据用户 3667217 的建议编辑代码以符合编程指南)

提醒:当您使用多重处理时,您需要在 'if __name__ == '__main__': 子句中启动一个进程:(参见 programming guidelines

import pickle
import multiprocessing

class Tricky:
    def __init__(self,x):
        self.data=x

    def __setstate__(self, d):
        print('setstate happening')
        self.data = 10

    def __getstate__(self):
        return self.data
        print('getstate happening')

def report(ar,q):
    q.put(ar.data)

if __name__ == '__main__':
    ar = Tricky(5)
    q = multiprocessing.Queue()
    p = multiprocessing.Process(target=report, args=(ar, q))
    print('now starting process')
    p.start()
    print('now joining process')
    p.join()
    print('now getting results from queue')
    print(q.get())
    print('now getting pickle dumps')
    print(pickle.loads(pickle.dumps(ar)).data)   

在 windows 上,我看到了

now starting process
now joining process
setstate happening
now getting results from queue 
10
now getting pickle dumps
setstate happening
10

在 Ubuntu 上,我看到:

now starting process
now joining process
now getting results from queue
5
now getting pickle dumps
getstate happening
setstate happening
10

我想这应该可以回答您的问题。 multiprocess 在 Windows 上调用 __setstate__ 方法,但不在 Linux 上调用。在 Linux 上,当你调用 pickle.dumps 时,它首先调用 __getstate__,然后是 __setstate__。有趣的是,多处理模块在不同平台上的行为有何不同。

多处理模块可以通过以下三种方式之一启动:spawn、fork 或 forkserver。默认情况下,在 unix 上,它会分叉。这意味着在新进程诞生时不需要 pickle 任何已经加载到 ram 中的东西。

如果您需要更直接地控制分叉的发生方式,您需要将启动设置更改为生成。为此,创建上下文

ctx=multiprocessing.get_context('spawn')

并将所有对 multiprocessing.foo() 的调用替换为对 ctx.foo() 的调用。当您这样做时,每个新进程都会作为一个新的 python 实例诞生;发送到其中的所有内容都将通过 pickle 发送,而不是直接 memcopy。