为何asyncio.gather()在单个任务失败时不取消待处理任务?
解决asyncio.gather异常时未取消剩余协程的问题
问题原因
默认情况下,asyncio.gather会等待所有传入的协程/任务完成,哪怕其中某个抛出异常。它只会在所有任务结束后,抛出第一个遇到的异常,不会主动取消其他正在运行的任务。
解决方案
有两种常见方式实现“某个任务异常时取消剩余任务”的需求:
方式1:手动创建Task并在捕获异常后取消所有任务
先把协程包装成asyncio.Task对象,这样可以在捕获异常时主动取消所有未完成的任务:
import asyncio async def worker(id): await asyncio.sleep(1) if id == 2: raise ValueError("Failing task") return id async def main(): # 先创建Task对象,而非直接传协程 tasks = [asyncio.create_task(worker(i)) for i in range(5)] try: await asyncio.gather(*tasks) except Exception as e: print("Error:", e) # 取消所有未完成的任务 for task in tasks: if not task.done(): task.cancel() # 等待取消完成(避免程序退出警告) await asyncio.gather(*tasks, return_exceptions=True) asyncio.run(main())
方式2:使用asyncio.wait配合return_when参数
asyncio.wait可以设置return_when=FIRST_EXCEPTION,这样一旦有任务抛出异常就立即返回,此时可取消剩余任务:
import asyncio async def worker(id): await asyncio.sleep(1) if id == 2: raise ValueError("Failing task") return id async def main(): tasks = [asyncio.create_task(worker(i)) for i in range(5)] done, pending = await asyncio.wait(tasks, return_when=asyncio.FIRST_EXCEPTION) # 检查异常任务 for task in done: try: task.result() except Exception as e: print("Error:", e) # 取消所有待完成任务 for p in pending: p.cancel() # 等待取消完成 await asyncio.gather(*pending, return_exceptions=True) return # 所有任务正常完成时收集结果 results = [task.result() for task in done] print("Results:", results) asyncio.run(main())
补充说明
- 如果使用
asyncio.gather(return_exceptions=True),所有异常会被当作返回值返回,不会抛出,此时也不会取消其他任务,适合需要收集所有结果(包括异常)的场景,但不符合“异常时取消剩余任务”的需求。 - 手动取消任务后,最好用带
return_exceptions=True的gather等待任务完成,避免程序退出时出现未完成任务的警告。
内容的提问来源于stack exchange,提问作者Ritu Raj Aazad
相关产品推荐
相关产品推荐

