Python asyncio:处理gather()中的异常-文档不清楚?

ami*_*itr 17 python python-3.x

asyncio.gather文档

如果 return_exceptions 是False(默认),第一个引发的异常会立即传播到等待 on 的任务gather()。aws 序列中的其他等待对象不会被取消而是会继续运行。

但是,从一个简单的测试看来,如果其中一个任务在 return_exceptions 时引发异常,则False所有其他等待对象都将被取消(或者更准确地说,如果我不清楚术语,其他等待对象不会完成它们的工作):

import asyncio

async def factorial(name, number, raise_exception=False):
    # If raise_exception is True, will raise an exception when
    # the loop counter > 3
    f = 1
    for i in range(2, number + 1):
        print(f'  Task {name}: Compute factorial({i})...')

        if raise_exception and i > 3:
            print(f'  Task {name}: raising Exception')
            raise Exception(f'Bad Task {name}')

        await asyncio.sleep(1)
        f *= i
    print(f'==>> Task {name} DONE: factorial({number}) = {f}')
    return f

async def main():
    tasks = [factorial('A', 5),  # this will not be finished
             factorial('B', 10, raise_exception=True),
             factorial('C', 2)]

    try:
        results = await asyncio.gather(*tasks)
        print('Results:', results)
    except Exception as e:
        print('Got an exception:', e)

asyncio.run(main())
Run Code Online (Sandbox Code Playgroud)

这段代码在做什么,只是为了让它更简单,它定义了 3 个任务并调用asyncio.gather()它们。其中一个任务在其他任务完成之前引发异常,而另一个任务没有完成。

实际上,我什至无法理解文档所说的内容 - 如果等待的任务引发并捕获异常gather,我什至无法获得返回的结果(即使其他任务会以某种方式完成) .

我是否遗漏了什么,或者文档有问题?

这是用 Python 3.7.2 测试的。

gdl*_*lmx 5

正如文档中所预期的那样,我已经运行了您的代码并获得了以下输出。

  Task C: Compute factorial(2)...
  Task A: Compute factorial(2)...
  Task B: Compute factorial(2)...
==>> Task C DONE: factorial(2) = 2
  Task A: Compute factorial(3)...
  Task B: Compute factorial(3)...
  Task A: Compute factorial(4)...
  Task B: Compute factorial(4)...
  Task B: raising Exception
Got an exception: Bad Task B
  Task A: Compute factorial(5)...
==>> Task A DONE: factorial(5) = 120
Run Code Online (Sandbox Code Playgroud)

这是怎么回事

  1. 任务A、B、C被提交到队列中;
  2. 所有任务都在运行,而 C 最早完成。
  3. 任务 B 引发和异常。
  4. await asyncio.gater()立即返回,并print('Got an exception:', e)在屏幕上。
  5. 任务 A 继续运行并打印“==>> 任务 A 完成...”

你的测试有什么问题

正如@deceze 所评论的,您的程序在异常被捕获并main()返回后立即退出。因此,任务 A 和 C 被终止是因为整个进程死亡,而不是因为取消。

要修复它,请添加await asyncio.sleep(20)main()函数的末尾。

  • 如果你设置了 `return_exceptions=True`,异常对象将被返回并放入 `gather()` 的 `results` 中。对于您的示例,它将打印出“Results: [120, Exception('Bad Task B',), 2]”。简而言之,您可以信任`gather()` 的返回值。 (2认同)