多进程固有的共享内存不再适用于 python 3.10(来自 3.6)

Lam*_*ade 6 python shared-memory multiprocessing python-3.x

我知道有多种技术可以在 python 的进程之间共享内存和数据结构。这个问题专门针对 python 脚本中固有的共享内存,该内存存在于 python 3.6 中,但在 3.10 中似乎不再存在。 有谁知道为什么以及是否可以在 3.10 中恢复此功能?或者说我观察到的这个变化是什么? 我已将 Mac 升级到 Monterey,但它不再支持 python 3.6,因此我被迫升级到 3.9 或 3.10+。

注意:我倾向于在 Mac 上开发并在 Ubuntu 上运行生产。不确定这是否在这里。从历史上看,在 3.6 中,无论操作系统如何,一切行为都相同。

使用以下 python 文件制作一个简单的项目

我的图书馆.py

MyDict = {}
Run Code Online (Sandbox Code Playgroud)

测试.py

import threading
import time
import multiprocessing

import myLibrary


def InitMyDict():
    myLibrary.MyDict = {'woot': 1, 'sauce': 2}
    print('initialized myLibrary.MyDict to ', myLibrary.MyDict)


def MainLoop():
    numOfSubProcessesToStart = 3
    for i in range(numOfSubProcessesToStart):
        t = threading.Thread(
            target=CoolFeature(),
            args=())
        t.start()

    while True:
        time.sleep(1)


def CoolFeature():
    MyProcess = multiprocessing.Process(
        target=SubProcessFunction,
        args=())
    MyProcess.start()


def SubProcessFunction():
    print('SubProcessFunction: ', myLibrary.MyDict)


if __name__ == '__main__':
    InitMyDict()
    MainLoop()
Run Code Online (Sandbox Code Playgroud)

当我在 3.6 上运行它时,它的行为与 3.10 显着不同。我确实知道子进程无法修改主进程的内存,但是访问之前设置的主进程的数据结构仍然非常方便,而不是将每个小东西移动到共享内存中只是为了读取一个简单的数据字典/整数/字符串/等。

Python 3.10 输出:

python3.10 test.py 
initialized myLibrary.MyDict to  {'woot': 1, 'sauce': 2}
SubProcessFunction:  {}
SubProcessFunction:  {}
SubProcessFunction:  {}
Run Code Online (Sandbox Code Playgroud)

Python 3.6 输出:

python3.6 test.py 
initialized myLibrary.MyDict to  {'woot': 1, 'sauce': 2}
SubProcessFunction:  {'woot': 1, 'sauce': 2}
SubProcessFunction:  {'woot': 1, 'sauce': 2}
SubProcessFunction:  {'woot': 1, 'sauce': 2}
Run Code Online (Sandbox Code Playgroud)

观察:

请注意,在 3.6 中,子流程可以查看主流程设置的值。但在 3.10 中,子进程看到一个空字典。

Rol*_*ith 10

简而言之,从3.8开始,CPython在MacOs上使用spawn start方法。之前它使用的是fork方法。

\n

在 UNIX 平台上,使用fork启动方法,这意味着每个新multiprocessing进程都是 fork 时父进程的精确副本。

\n

spawn方法意味着它为每个新multiprocessing进程启动一个新的 Python 解释器。根据文档:

\n
\n

子进程只会继承运行进程对象\xe2\x80\x99srun()方法所需的资源。

\n
\n

它将把你的程序导入到这个新的解释器中,因此启动进程等只能从 -block 中完成if __name__ == \'__main__\':

\n

这意味着您不能指望父进程中的变量在子进程中可用,除非它们是要导入的模块级常量

\n

所以这个变化是重大的。

\n

可以做什么?

\n

如果所需的信息可以是模块级常量,那就可以以最简单的方式解决问题。

\n

如果这是不可能的(例如,因为需要在运行时生成数据),您可以让父级将要共享的信息写入文件。例如,以 JSON 格式并在启动其他进程之前。然后孩子们就可以简单地阅读这个内容。这可能是下一个最简单的解决方案。

\n

使用 amultiprocessing.Manager可以让您在进程之间共享 a dict。然而,存在与此相关的一定量的开销。

\n

或者您可以尝试multiprocessing.set_start_method("fork")在创建进程或池之前调用,看看它是否不会在您的情况下崩溃。这将恢复到 MacO 上 3.8 之前的方法。但正如这个 bug 中记录的那样,在 MacO 上使用该fork方法确实存在问题。\n阅读该问题表明,只要不使用线程,fork 就可能没问题。

\n