在Python的asyncio异步编程场景中,gather函数是用于并发运行多个异步任务的常用工具,默认情况下,只要有一个任务抛出异常,gather就会立即终止剩余任务并抛出第一个异常,这会导致其他任务的异常被忽略。如果需要收集所有任务的异常,就需要调整gather的运行参数和异常处理逻辑。

gather的默认异常处理逻辑
先看默认情况下gather的行为,我们创建三个异步任务,其中两个会抛出异常,观察执行效果:
import asyncio
async def task1():
await asyncio.sleep(1)
raise ValueError("task1发生值错误")
async def task2():
await asyncio.sleep(2)
return "task2执行成功"
async def task3():
await asyncio.sleep(1.5)
raise TypeError("task3发生类型错误")
async def main():
try:
# 默认不设置return_exceptions参数
results = await asyncio.gather(task1(), task2(), task3())
print("执行结果:", results)
except Exception as e:
print("捕获到异常:", type(e).__name__, str(e))
if __name__ == "__main__":
asyncio.run(main())
运行上述代码后,只会抛出ValueError异常,task3的TypeError异常会被忽略,task2也不会继续执行,这就是gather的默认行为。
使用return_exceptions参数收集所有异常
gather函数提供了一个return_exceptions参数,默认值为False,当将其设置为True时,gather不会在任务抛出异常时中断,而是会等待所有任务执行完成,把正常结果和异常对象都放到返回的结果列表中。此时如果任务正常执行,结果列表中对应位置是任务的返回值;如果任务抛出异常,对应位置是该异常对象。
修改上面的main函数,添加return_exceptions=True参数:
async def main():
# 设置return_exceptions为True
results = await asyncio.gather(task1(), task2(), task3(), return_exceptions=True)
print("所有任务结果:", results)
# 遍历结果,区分正常结果和异常
for idx, res in enumerate(results):
if isinstance(res, Exception):
print(f"任务{idx+1}发生异常: {type(res).__name__} {str(res)}")
else:
print(f"任务{idx+1}执行成功: {res}")
运行修改后的代码,输出结果如下:
所有任务结果: [ValueError('task1发生值错误'), 'task2执行成功', TypeError('task3发生类型错误')]
任务1发生异常: ValueError task1发生值错误
任务2执行成功: task2执行成功
任务3发生异常: TypeError task3发生类型错误
可以看到所有任务都执行完成了,两个异常都被收集到了结果列表中,没有抛出第一个异常就中断执行。
注意事项
return_exceptions=True仅收集被gather调度的任务的异常,如果gather本身调用过程中发生异常(比如传入的不是协程对象),还是会直接抛出。- 结果列表中的异常对象是原始的异常实例,可以正常调用其属性获取异常信息,比如
res.args可以获取异常的参数。 - 如果需要同时处理多个异常,可以遍历结果列表后,把收集到的异常统一封装后再抛出,或者记录到日志中。
统一处理多个异常的示例
如果需要把收集到的所有异常统一抛出,可以自定义一个异常类来包装多个异常:
class MultiException(Exception):
def __init__(self, exceptions):
self.exceptions = exceptions
# 拼接所有异常的提示信息
msg = "; ".join([f"{type(ex).__name__}: {str(ex)}" for ex in exceptions])
super().__init__(msg)
async def main():
results = await asyncio.gather(task1(), task2(), task3(), return_exceptions=True)
exceptions = [res for res in results if isinstance(res, Exception)]
if exceptions:
raise MultiException(exceptions)
print("所有任务都执行成功:", results)
if __name__ == "__main__":
try:
asyncio.run(main())
except MultiException as e:
print("捕获到多个异常:", e)
for ex in e.exceptions:
print(f"异常详情: {type(ex).__name__} {str(ex)}")
这样就能在收集所有异常后,统一抛出处理,避免遗漏任何一个任务的异常情况。
asynciogather异常收集async_await修改时间:2026-06-24 04:00:25