为什么multiprocessing.Process在windows和linux上对全局对象和函数参数的行为不同

Pat*_*ick 13 python multiprocessing

在windows和linux上运行时,以下代码具有不同的输出(均使用python2.7)

'''import_mock.py'''
to_mock = None
Run Code Online (Sandbox Code Playgroud)
'''test.py'''
import import_mock
from multiprocessing import Process

class A(object):
    def __init__(self):
        self.a = 1
        self.b = 2
        self.c = 3

    def __getstate__(self):
        print '__getstate__'
        return { 'a': self.a, 'b': self.b,
                 'c':0 }

def func():
    import_mock.to_mock = 1
    a = A()
    return a

def func1(a):
    print a.a, a.b, a.c
    print import_mock.to_mock


if __name__ == '__main__':
    a = func()
    p = Process(target=func1, args=(a,))
    p.start()
    p.join()
Run Code Online (Sandbox Code Playgroud)

在Windows上,输出是:

__getstate__
1 2 0
None
Run Code Online (Sandbox Code Playgroud)

这是我的预期

在linux上,它是:

1 2 3
1
Run Code Online (Sandbox Code Playgroud)

哪个不克隆全局对象和传递的args.

我的问题是他们为什么表现不同?如何让linux代码与windows one相同?

Blc*_*ght 19

在Linux(和其他类Unix操作系统)上,Python的multiprocessing模块fork()用于创建新的子进程,有效地继承父进程的内存状态的副本.这意味着解释并不需要咸菜正在作为传递的对象Processargs,因为子进程将已经有他们在正常的形式提供.

fork()但是,Windows没有系统调用,因此multiprocessing模块需要做更多的工作才能使子生成过程正常工作.在fork()基于实施来到第一,和非分叉Windows实现后来.

值得注意的是,Python开发人员经常觉得,根据您运行Python的平台,创建子进程的程度有点不同.因此,在Python 3.4中,添加了一个新系统,允许您选择您希望使用的启动方法.选项是"fork","forkserver""spawn".该"fork"方法仍然是类Unix系统的默认设置(它是早期Python版本中唯一的实现).该"spawn"方法是Windows上的默认(且唯一)选项,但现在也可以在类Unix系统上使用.该"forkserver"方法是两者之间的混合(并且仅在某些类Unix系统上可用).您可以阅读有关文档中方法之间差异的更多信息.


Tim*_*ers 13

添加到@ Blckknght的答案:在Windows上,每个进程"从头开始"导入原始模块,而在Unix-y系统上,只有主进程运行整个模块,而所有其他进程看到当时存在的任何进程fork()用于创建新流程(不,不是在称fork()自己 - multiprocessing内部会在创建新流程时调用它).

详细地说,为您的import_mock:

  • 在所有平台上,主进程调用func(),设置import_mock.to_mock为1.

  • 在Unix-y平台上,这就是所有新进程看到的内容:在此fork()之后发生,因此1是所有新进程继承的状态.

  • 在Windows上,所有新进程都"从头开始"运行整个模块.所以他们每个人都进口自己的全新版本import_mock.只有主进程调用func(),因此只有主进程看到to_mock更改为1.所有其他进程都看到新None状态.

这都是预期的,第二次实际上很容易理解;-)

传递的a内容更微妙,因为它更多地取决于multiprocessing实现细节.实现可能已经选择从一开始就在所有平台上挑选参数,但事实并非如此,现在改变而不破坏某些平台上的东西为时已晚.

由于写时复制fork()语义,没有必要Process()在Unix-y系统上挑选参数,因此实现从未这样做过.但是,没有fork()它就必须在Windows上腌制它们 - 所以实现就是这样.

在Python 3.4之前,它允许你spawn在所有平台上强制"Windows实现"(),没有机械方法来避免可能的跨平台意外.

但在实践中,我很少被这个困扰. 据了解,例如,多处理可能在很大程度上取决于酸洗,我完全不知道在哪里附近玩腌菜.你遇到"问题"传递一个A()实例的唯一原因是你正在玩泡菜技巧(通过覆盖默认值__getstate__()).