对于以下脚本(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())
答案 0 :(得分:1)
concurrent.futures.ProcessPoolExecutor
使用multiprocessing
模块进行多重处理。
并且,正如Programming guidelines中所述,这意味着您必须保护您不希望在__main__
块中的每个进程中运行的任何顶级代码:
确保新的Python解释器可以安全地导入主模块,而不会导致意外的副作用(例如启动新进程)。
......应该使用
来保护程序的“切入点”if __name__ == '__main__':
...
请注意,仅在使用spawn
或forkserver
start methods时才需要这样做。但如果您使用的是Windows,spawn
是默认设置。并且,无论如何,它永远不会伤害来执行此操作,并且通常会使代码更清晰,因此无论如何都值得做。
您可能不希望以这种方式保护您的import
。毕竟,每个核心调用import pandas as pd
一次的成本可能看起来很重要,但这只会在启动时发生,并且运行数百次CPU负载功能的成本将完全淹没它。 (如果没有,你可能不想首先使用多处理......)通常,def
和class
语句也是如此(特别是如果它们没有捕获任何闭包变量或任何事情)。它只是多次运行的设置代码不正确(例如示例中的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
。