生成新进程时导入会发生什么?

时间:2015-03-23 04:05:25

标签: python module multiprocessing py4j

生成新进程时导入的模块变量会发生什么变化?

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模块吗?或者有更好的方法吗?

2 个答案:

答案 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答案的回答。