生成新进程时导入的模块变量会发生什么变化?
IE
with concurrent.futures.ProcessPoolExecutor(max_workers=settings.MAX_PROCESSES) as executor:
for stuff in executor.map(foo, paths):
其中:
def foo(str):
x = someOtherModule.fooBar()
foobar正在访问someOtherModule开头声明的内容:
someOtherModule.py:
myHat='green'
def fooBar():
return myHat
具体来说,我有一个模块(称为Y),它在任何函数之外的顶部初始化了py4j网关。在模块X中,我一次加载多个文件,加载后对数据进行排序的函数使用Y中的函数,该函数又使用网关。
这个设计是pythonic吗? 在每个新进程生成后,我应该导入我的Y模块吗?或者有更好的方法吗?
答案 0 :(得分:3)
在Linux上,fork
将用于生成子项,因此父项的全局范围内的任何内容也将在子项中可用,并具有写时复制语义。
在Windows上,父进程的import
模块中模块级别的任何__main__
都将在子进程中重新导入。
这意味着如果你有一个父模块(我们称之为someModule
),就像这样:
import someOtherModule
import concurrent.futures
def foo(str):
x = someOtherModule.fooBar()
if __name__ == "__main__":
with concurrent.futures.ProcessPoolExecutor(max_workers=settings.MAX_PROCESSES) as executor:
for stuff in executor.map(foo, paths):
# stuff
someOtherModule
看起来像这样:
myHat='green'
def fooBar():
return myHat
在此示例中,someModule
是脚本的__main__
模块。因此,在Linux上,您在子级中获得的myHat
实例将是someModule
中的{-1}}版本。在Windows上,每个子流程在加载后都会重新导入someModule
,这也会导致someOtherModule
重新导入。
我不太了解py4j Gateway
对象,以确定您是否确定这是否是您想要的行为。如果Gateway
对象是可挑选的,您可以明确地将其传递给每个孩子,但您必须使用multiprocessing.Pool
代替concurrent.futures.ProcessPoolExecutor
:
import someOtherModule
import multiprocessing
def foo(str):
x = someOtherModule.fooBar()
def init(hat):
someOtherModule.myHat = hat
if __name__ == "__main__":
hat = someOtherModule.myHat
pool = multiprocessing.Pool(settings.MAX_PROCESSES,
initializer=init, initargs=(hat,))
for stuff in pool.map(foo, paths):
# stuff
但是,您似乎并不需要为用例执行此操作。您可能正好使用重新导入。
答案 1 :(得分:2)
当你创建一个新进程时,会调用fork()
,它会克隆整个进程和堆栈,内存空间等。这就是多处理被认为比多线程更昂贵的原因,因为复制很昂贵
所以回答你的问题,所有"导入的模块变量"克隆。您可以根据需要修改它们,但原始父进程不会看到此更改。
修改强> 这仅适用于基于Unix的系统。请参阅Dano对Unix + Windows答案的回答。