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

What happens to imports when a new process is spawned?

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

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

当您创建一个新进程时,会调用一个 fork(),它会克隆整个进程和堆栈、内存 space 等。这就是为什么多处理被认为比多处理更昂贵的原因线程,因为复制很昂贵。

所以为了回答您的问题,所有 "imported module variables" 都被克隆了。您可以随意修改它们,但您的原始父进程不会看到此更改。

编辑: 这仅适用于基于 Unix 的系统。请参阅 Dano 对 Unix+Windows 的回答。

在 Linux 上,fork 将用于生成 child,因此 parent 全局范围内的任何内容也将在 child,具有 copy-on-write 语义。

在 Windows 上,您 import 在 parent 进程的 __main__ 模块中 module-level 的任何内容都将在 re-imported child.

这意味着如果您有一个 parent 模块(我们称它为 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 上,您在 child 中获得的 myHat 实例将是 someModule 中实例的 copy-on-write 版本。在 Windows 上,每个 child 进程一加载就会 re-import someModule,这将导致 someOtherModule 也成为 re-imported。

我对 py4j 的了解还不够 Gateway objects 无法判断您是否确定这是您想要的行为。如果 Gateway object 是可腌制的,您可以显式地将其传递给每个 child,但您必须使用 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

不过,您似乎不需要为您做这件事 use-case。使用 re-import.

可能没问题