我在 concurrent.futures 中遇到 ProcessPoolExecutor 问题

I am having problems with ProcessPoolExecutor from concurrent.futures

我有一个大代码需要一段时间来计算,我决定学习 多线程多处理 因为只有 20我的处理器的 % 被用来进行计算。 多线程没有任何改进后,我决定尝试多处理,每当我尝试使用它时,它只会显示很多错误甚至在一个非常简单的代码上。

这是我的大计算量代码开始出现问题后测试的代码:

from concurrent.futures import ProcessPoolExecutor

def func():
    print("done")

def func_():
    print("done")

def main():
    executor = ProcessPoolExecutor(max_workers=3)

    p1 = executor.submit(func)
    p2 = executor.submit(func_)

main()

在我收到的错误消息中说

An attempt has been made to start a new process before the
        current process has finished its bootstrapping phase.

        This probably means that you are not using fork to start your
        child processes and you have forgotten to use the proper idiom
        in the main module:

            if __name__ == '__main__':
                freeze_support()
                ...

这不是完整的消息因为它非常大但我认为我可能会有所帮助以帮助我。错误消息中的几乎所有其他内容就像“行错误...在...”

如果可能有帮助,大代码位于:https://github.com/nobody48sheldor/fuseeinator2.0 它可能不是最新版本。

我更新了您的代码以显示正在调用 main。这是像 Windows 这样的衍生操作系统的问题。要在我的 linux 机器上进行测试,我必须添加一些代码。但这在我的机器上崩溃了:

# Test code to make linux spawn like Windows and generate error. This code 
# # is not needed on windows.
if __name__ == "__main__":
    import multiprocessing as mp
    mp.freeze_support()
    mp.set_start_method('spawn')

# test script
from concurrent.futures import ProcessPoolExecutor

def func():
    print("done")

def func_():
    print("done")

def main():
    executor = ProcessPoolExecutor(max_workers=3)
    p1 = executor.submit(func)
    p2 = executor.submit(func_)

main()

在生成系统中,python 不能直接分叉到新的执行上下文中。相反,它 运行 是 python 解释器的新实例,导入模块和 pickles/unpickles 足够的状态来创建子执行环境。这可能是一个非常繁重的操作。

但是您的脚本导入不安全。由于 main() 是在模块级别调用的,因此子项中的导入将再次 运行 main 。这将创建一个孙子进程,它 运行 又是 的主要 (等等,直到你挂掉你的机器)。 Python 检测到此无限循环并改为显示消息。

顶级脚本总是被称为"__main__"。将所有应该只 运行 一次的代码放在 if 中的脚本级别。如果模块是导入的,没有什么害处 运行.

if __name__ == "__main__":
    main()

脚本将起作用。

有代码分析器可以导入模块来提取文档字符串或其他有用的东西。您的代码不应该仅仅因为某些工具进行了导入就发射导弹。

解决该问题的另一种方法是将与多处理相关的所有内容从脚本中移出并放入模块中。假设我有一个包含你的代码的模块

whatever.py

from concurrent.futures import ProcessPoolExecutor

def func():
    print("done")

def func_():
    print("done")

def main():
    executor = ProcessPoolExecutor(max_workers=3)

    p1 = executor.submit(func)
    p2 = executor.submit(func_)

myscript.py

#!/usr/bin/env pythnon3
import whatever
whatever.main()

现在,由于池已在导入的模块中准备就绪,该模块本身不会执行这种疯狂的重启操作,因此不需要 if __name__ == "__main__":。无论如何把它放在 myscript.py 中是个好主意,但不是必需的。