为什么分叉进程 Linux 时子进程会继承 `sys.modules` 而在 Macos 中却没有

Why does the child process inherit `sys.modules` when forking processes Linux but not in Macos

在不同的平台上使用多处理时,sys.modules的处理方式似乎有所不同。如果您在 Linux 上修改 sys.modules 字典并生成一个子进程,该进程似乎继承了字典,但在 Macos 中会为 sys.modules 创建一个新对象。这是因为多处理的工作方式不是 OS 不可知的吗?

这是因为 Linux 在创建子进程时使用 fork() 而 Mac 的做法不同吗?

例如,如果我尝试这个片段:

import multiprocessing
import sys
import time
from types import ModuleType


def worker():
    # print the id of the dict
    print('worker', id(sys.modules), 'foo' in sys.modules)


def main():
    # modify the sys.modules dict
    sys.modules['foo'] = ModuleType('foo')

    # print the id of the dict
    print('main', id(sys.modules), 'foo' in sys.modules)

    p = multiprocessing.Process(target=worker, daemon=True)
    p.start()

    time.sleep(0.1)


if __name__ == '__main__':
    main()

我得到这些结果:

Linux (x86_64, python 3.10.1)

main 139897509461248 True
worker 139897509461248 True

Macos(M1 ARM,python 3.10.1)

main 4307217856 True
worker 4334595520 False

对于上下文:我正在尝试仅使用 stdlib(ala Celery)构建任务队列。但是,我找不到将腌制函数传递给工作进程的惯用方法,但在大多数情况下,我发现在取消序列化函数时属性查找失败(在序列化可调用对象时,pickle 仅存储名称和模块,并且已加载在运行时反序列化)。

我正在尝试的解决方法是在序列化函数时更改函数的 __module__ 属性,并将其添加到动态创建的模块中。这似乎修复了属性查找错误,但感觉像是一个糟糕的 hack。

在 MacOS 上,新的 multi-processing 进程默认生成而不是分叉。这效率较低,并且包含 re-import 个包和模块。

来自docs

Changed in version 3.8: On macOS, the spawn start method is now the default. The fork start method should be considered unsafe as it can lead to crashes of the subprocess. See bpo-33725.

您可以尝试 multiprocessing.set_start_method(),风险自负。