以下代码在Windows和Linux(都使用python2.7)上运行时有不同的输出
'''import_mock.py'''
to_mock = None
'''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()
在Windows上,输出为:
__getstate__
1 2 0
None
这是我的预期
在linux上,它是:
1 2 3
1
哪个不克隆全局对象和传递的args。
我的问题是他们的行为有何不同?如何使linux代码与windows one相同?
答案 0 :(得分:18)
在Linux(和其他类Unix操作系统)上,Python的multiprocessing
模块使用fork()
创建新的子进程,有效地继承父进程内存状态的副本。这意味着解释器不需要挑选作为Process
的{{1}}传递的对象,因为子进程已经以正常形式提供它们。
Windows没有args
系统调用,因此fork()
模块需要做更多工作才能使子生成过程正常工作。基于multiprocessing
的实现首先出现,而非分叉的Windows实现则是后来的。
值得注意的是,Python开发人员经常觉得,根据您运行Python的平台,创建子进程的程度有点不同。因此,在Python 3.4中,添加了一个新系统,允许您select the start method that you would prefer to use。选项包括fork()
,"fork"
和"forkserver"
。 "spawn"
方法仍然是类Unix系统的默认方法(它是早期Python版本中唯一的实现)。 "fork"
方法是Windows上的默认(且唯一)选项,但现在也可以在类Unix系统上使用。 "spawn"
方法是两者之间的混合(并且仅在某些类Unix系统上可用)。您可以阅读有关文档中方法之间差异的更多信息。
答案 1 :(得分:12)
添加到@ Blckknght的答案:在Windows上,每个进程都从头开始导入原始模块"而在Unix-y系统上只有主进程运行整个模块,而所有其他进程流程会看到当fork()
用于创建新流程时存在的任何内容(不,您自己没有调用fork()
- multiprocessing
内部调用无论何时创建新流程。)
详情请参阅import_mock
:
在所有平台上,主流程调用func()
,将import_mock.to_mock
设置为1。
在Unix-y平台上,这是所有新进程看到的内容:fork()
之后发生,所以1是所有新进程继承的状态。
在Windows上,所有新进程都从头开始运行整个模块"#34;。因此,他们每个人都导入自己的全新版import_mock
。只有主流程调用func()
,因此只有主流程会将to_mock
更改为1.所有其他流程都会看到新的None
州。
这一切都是预期的,第二次实际上很容易理解; - )
传递a
后发生的事情更微妙,因为它更多地取决于multiprocessing
实施细节。实现可以从一开始就选择在所有平台上挑选参数,但它没有,现在改变而且不会破坏 some <上的东西为时已晚/ em>平台。
由于写时复制fork()
语义,在Unix-y系统上挑选Process()
参数并不是必要的,因此实现永远不会没有。但是,如果没有fork()
,就必须在Windows上对它们进行腌制 - 因此实现方式确实如此。
在Python 3.4之前,它允许你强制执行Windows实现&#34; (spawn
)在所有平台上,没有机械方法可以避免可能的跨平台意外。
但在实践中,我很少被这个打扰。 知道,例如,多处理可能在很大程度上取决于酸洗,我完全不知道在哪里附近玩腌菜。你遇到问题的唯一原因&#34;通过A()
实例是你正在玩泡菜技巧(通过覆盖默认的__getstate__()
)。