Python current.futures多次导入库(多次在顶级作用域中执行代码)

时间:2018-06-12 23:10:47

标签: python cpu python-multiprocessing python-multithreading concurrent.futures

对于以下脚本(python 3.6,windows anaconda),我注意到导入的库的数量与调用处理器的数量一样多。 print('Hello')也会执行多次相同的次数。

我认为处理器只会被调用func1而不是整个程序。实际的func1是一个繁重的cpu限制任务,将执行数百万次。

这是否适合此类任务的框架?

import pandas as pd
import numpy as np
from concurrent.futures import ProcessPoolExecutor

print("Hello")

def func1(x):
    return x


if __name__ == '__main__':
    print(datetime.datetime.now())    
    print('test start')

    with ProcessPoolExecutor() as executor:
        results = executor.map(func1, np.arange(1,1000))
        for r in results:
            print(r)

    print('test end')
    print(datetime.datetime.now())

1 个答案:

答案 0 :(得分:1)

concurrent.futures.ProcessPoolExecutor使用multiprocessing模块进行多重处理。

并且,正如Programming guidelines中所述,这意味着您必须保护您不希望在__main__块中的每个进程中运行的任何顶级代码:

  

确保新的Python解释器可以安全地导入主模块,而不会导致意外的副作用(例如启动新进程)。

     

......应该使用if __name__ == '__main__': ...

来保护程序的“切入点”

请注意,仅在使用spawnforkserver start methods时才需要这样做。但如果您使用的是Windows,spawn是默认设置。并且,无论如何,它永远不会伤害来执行此操作,并且通常会使代码更清晰,因此无论如何都值得做。

您可能希望以这种方式保护您的import。毕竟,每个核心调用import pandas as pd一次的成本可能看起来很重要,但这只会在启动时发生,并且运行数百次CPU负载功能的成本将完全淹没它。 (如果没有,你可能不想首先使用多处理......)通常,defclass语句也是如此(特别是如果它们没有捕获任何闭包变量或任何事情)。它只是多次运行的设置代码不正确(例如示例中的print('hello'))需要保护。

concurrent.futures doc(以及PEP 3148)中的示例都通过使用“main function”惯用法来处理:

def main():
    # all of your top-level code goes here

if __name__ == '__main__':
    main()

这样做的另一个好处是可以将顶级全局变量转换为本地变量,以确保不会意外地分享它们(这可能会导致multiprocessing出现问题,而fork实际上与{spawn共享1}},但是使用fork复制,因此在一个平台上进行测试时,相同的代码可能会有效,但在另一个平台上部署时则会失败。

如果你想知道为什么会发生这种情况:

使用multiprocessing start方法,concurrent.futures通过克隆父Python解释器创建每个新子进程,然后在您(或spawn)的位置启动池服务功能创建了游泳池。因此,顶级代码不会重新运行。

使用multiprocessing start方法,import通过启动一个干净的新Python解释器,import代码,然后启动池服务功能来创建每个新的子进程。因此,顶级代码将作为https://graph.facebook.com/v3.0/<PAGE_ID>/feed

的一部分重新运行