如何确保所有任务都使用 asyncio.gather 执行?

How to ensure all task are executed using asyncio.gather?

当使用 asyncio.gather 时,当其中一个任务引发异常时,所有任务都不会执行:

# coding: utf-8
import asyncio


async def foo(i):
    await asyncio.sleep(0.1)
    if i == 2:
        print("2: 1/0")
        1/0
    print(i)


async def main():
    futures = []
    for i in range(1000):
        futures.append(foo(i))
    await asyncio.gather(*futures)


asyncio.run(main())
0
1
2: 1/0
3
4
5
6
7
[...]
501
502
503
504
Traceback (most recent call last):
  File "<input>", line 24, in <module>
  File "/usr/lib/python3.8/asyncio/runners.py", line 43, in run
    return loop.run_until_complete(main)
  File "/usr/lib/python3.8/asyncio/base_events.py", line 616, in run_until_complete
    return future.result()
  File "<input>", line 21, in main
  File "<input>", line 9, in foo
ZeroDivisionError: division by zero

如何确保在退出 asyncio.gather 之前执行所有任务?我知道这不可能是 asyncio.gather 的默认行为,因为如果我的一项任务从未完成,则永远不会引发异常。我的问题更多:如何在继续之前执行收集任务池并等待所有 finished/raise?

我想你要密切关注 documentation for asyncio.gather:

If return_exceptions is False (default), the first raised exception is immediately propagated to the task that awaits on gather(). Other awaitables in the aws sequence won’t be cancelled and will continue to run.

If return_exceptions is True, exceptions are treated the same as successful results, and aggregated in the result list.

根据我的阅读,如果你像这样调用 asyncio.gather...

await asyncio.gather(*futures, return_exceptions=True)

...你应该得到你想要的行为。