生成器函数,导致在所有进程完成后捕获异常

时间:2019-08-01 06:29:33

标签: python-3.x iterator multiprocessing generator itertools

我写了这个简短的POC来帮助理解我遇到的问题,希望有人可以向我解释发生了什么,以及如何解决和/或提高效率。

我使用迭代器,itertools和生成器的目的是因为我不想在内存中存储一​​个巨大的列表,因为按比例放大列表将变得难以管理,并且我不想遍历整个列表每一次做某事。请注意,我对生成器,迭代器和多处理的想法还很陌生,并且今天就编写了这段代码,因此,如果您可以清楚地告诉我我想念工作流中这些东西是如何工作的,请怀念我,并帮助我代码更好。

您应该能够按原样运行代码,并查看我面临的问题。我正在期待,一旦捕获到异常,它就被引发并且脚本死了,但是我所看到的正在发生,捕获了异常,但其他过程仍在继续。

如果我注释掉generateRange生成器并创建一个虚拟列表并将其传递给futures = (map(executor.submit, itertools.repeat(execute), mylist)),则会捕获到异常并按预期退出脚本。

我的猜测是,生成器/迭代器必须在脚本死之前完成生成范围,据我所知,情况并非如此。

我之所以选择使用生成器函数/迭代器是因为您只能在需要它们时才能访问它们。

有没有办法让我停止生成器继续运行,并适当地引发异常。

这是我的POC:

import concurrent.futures

PRIMES = [0]*80

import time

def is_prime(n):
    print("Enter")
    time.sleep(5)
    print("End")
    1/0

child = []
def main():
    with concurrent.futures.ProcessPoolExecutor(max_workers=1) as executor:
        for i in PRIMES:
            child.append(executor.submit(is_prime, i))
        for future in concurrent.futures.as_completed(child):
            if future.exception() is not None:
                print("Throw an exception")
                raise future.exception()

if __name__ == '__main__':
    main()

编辑:我用更简单的方法更新了POC。

1 个答案:

答案 0 :(得分:1)

不可能立即取消正在运行的期货,但这至少可以做到,因此在引发异常之后仅运行少数几个进程:

import concurrent.futures                                                  

PRIMES = [0]*80                                                            

import time                                                                

def is_prime(n):                                                           
    print("Enter")                                                         
    time.sleep(5)                                                          
    print("End")                                                           
    1/0                                                                    

child = []                                                                 
def main():                                                                
    with concurrent.futures.ProcessPoolExecutor(max_workers=1) as executor:
        for i in PRIMES:                                                   
            child.append(executor.submit(is_prime, i))                     
        for future in concurrent.futures.as_completed(child):              
            if future.exception() is not None:                             
                for fut in child:                                          
                    fut.cancel()                                           
                print("Throw an exception")                                
                raise future.exception()                                   

if __name__ == '__main__':                                                 
    main()