我有很多任务要执行,并通过生成器提供结果。然而,使用ProcessPoolExecutor
和as_completed
将贪婪地评估结果并将它们全部存储在内存中。在生成器中存储一定数量的结果后,是否有方法阻止?
最佳答案
这样做的目的是把你想处理的东西分成块,我用的例子和ProcessPoolExecutor
documentation差不多。
import concurrent.futures
import math
import itertools as it
PRIMES = [
293,
171,
293,
773,
99,
5419,
293,
171,
293,
773,
99,
5419,
293,
171,
293,
773,
99,
5419]
def is_prime(n):
if n % 2 == 0:
return False
sqrt_n = int(math.floor(math.sqrt(n)))
for i in range(3, sqrt_n + 1, 2):
if n % i == 0:
return False
return True
def main():
with concurrent.futures.ProcessPoolExecutor() as executor:
for number, prime in zip(PRIMES, executor.map(is_prime, PRIMES)):
print('%d is prime: %s' % (number, prime))
def main_lazy():
chunks = map(lambda x: it.islice(PRIMES, x, x+4), range(0, len(PRIMES), 4))
with concurrent.futures.ProcessPoolExecutor() as executor:
results = zip(PRIMES,
it.chain.from_iterable(map(lambda x: executor.map(is_prime, x),
chunks)))
for number, prime in (next(results) for _ in range(4)):
print('%d is prime: %s' % (number, prime))
if __name__ == "__main__":
main_lazy()
注意
main
和main_lazy
之间的区别,让我们解释一下:而不是有一个我们想要处理的所有的列表,我把它分成大小为4的块(使用
itertools.islice
)是有用的,我们的想法是,不是与执行器映射整个列表,而是映射块。然后只要使用python3 lazymap
我们就可以将执行器调用惰性地映射到每个块。因此,我们知道executor.map
不是懒惰的,所以当我们请求它时,会立即对块进行评估,但是直到我们不请求其他块时,将不调用该块的executor.map
。如您所见,我只请求整个结果列表中的前4个元素,但由于我也使用了
itertools.chain
,它将只消耗第一个块中的元素,而不计算iterable的其余部分。因此,既然您想返回生成器,那么从
main_lazy
函数返回结果就很容易了,您甚至可以抽象块大小(可能需要一个好的函数来获取propper块,但这超出了范围):def main_lazy(chunk_size):
chunks = map(lambda x: it.islice(PRIMES, x, x+chunk_size), range(0, len(PRIMES), chunk_size))
with concurrent.futures.ProcessPoolExecutor() as executor:
results = zip(PRIMES,
it.chain.from_iterable(map(lambda x: executor.map(is_prime, x),
chunks)))
return results