Python parallel.futures:ProcessPoolExecutor无法正常工作

时间:2019-12-01 10:10:18

标签: python time concurrency subprocess concurrent.futures

我正在尝试使用ProcessPoolExecutor方法,但是它失败了。 这是一个使用失败的示例(计算两个数字的大除法)。 我不明白这是什么错误

def gcd(pair):
    a, b = pair
    low = min(a, b)
    for i in range(low, 0, -1):
        if a % i == 0 and b % i == 0:
            return i

numbers = [(1963309, 2265973), (2030677, 3814172),
           (1551645, 2229620), (2039045, 2020802)]
start = time()
pool = ProcessPoolExecutor(max_workers=2)
results = list(pool.map(gcd, numbers))
end = time()
print('Took %.3f seconds' % (end - start))

BrokenProcessPool:进程运行中或挂起时,进程池中的进程突然终止。

1 个答案:

答案 0 :(得分:0)

将代码更改为如下所示,它将起作用:

from time import time
from concurrent.futures import ProcessPoolExecutor
def gcd(pair):
    a, b = pair
    low = min(a, b)
    for i in range(low, 0, -1):
        if a % i == 0 and b % i == 0:
            return i

numbers = [(1963309, 2265973), (2030677, 3814172),
           (1551645, 2229620), (2039045, 2020802)]

def main():
    start = time()
    pool = ProcessPoolExecutor(max_workers=3)
    results = list(pool.map(gcd, numbers))
    end = time()
    print('Took %.3f seconds' % (end - start))


if __name__ == '__main__':
    main()

在支持fork()的系统上这不是必需的,因为您的脚本只被导入一次,然后每个启动的ProcessPoolExecutor进程都已经在全局名称空间中拥有了一个对象副本,例如{ {1}}功能。一旦分叉,他们将经历引导过程,从而开始运行其目标函数(在本例中为工作进程循环,该进程从进程池执行程序接受作业),并且它们从不返回原始状态。分叉的主模块中的代码。

相反,如果您使用的是Windows和OSX上默认的基于gcd的进程,则必须为每个工作进程从头开始一个新进程,并且如果它们必须重新执行< em> import 您的模块。但是,如果您的模块直接在模块主体中执行类似spawn的操作,而没有像ProcessPoolExecutor那样进行保护,那么他们将无法在不启动新的if __name__ == '__main__':的情况下导入您的模块。因此,您遇到的这个错误实际上是在阻止您创建无限的过程炸弹。

ProcessPoolExecutor的文档中提到了这一点:

  

ProcessPoolExecutor模块必须可由工作程序子进程导入。这意味着__main__在交互式解释器中将不起作用。

但是他们并没有真正弄清楚为什么ProcessPoolExecutor模块是“可导入的”是什么或意味着什么。当您使用Python编写一个简单的脚本并像__main__一样运行它时,您的脚本python foo.py的模块名称为foo.py,而不是名为__main__的模块。如果您foo,您会得到。在这种情况下,“可导入”实际上意味着可导入,而没有诸如产生新流程之类的主要副作用。