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

mwkjh3gx  于 2023-01-22  发布在  Python
关注(0)|答案(2)|浏览(191)

asyncio.gather的文档中说
如果return_exceptions为False(默认值),第一个引发的异常会立即传播到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())

这段代码所做的事情,为了简单起见,它定义了3个任务,并对它们调用asyncio.gather(),其中一个任务在其他任务完成之前引发了异常,而另一个任务还没有完成。
实际上,我甚至无法理解文档中所说的内容--如果gather上等待的任务引发并捕获了异常,我甚至无法获得返回的结果(即使其他任务以某种方式完成了)。
我是否遗漏了什么,或者文档有问题?
这是用Python 3.7.2测试过的。

ars1skjm

ars1skjm1#

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

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

∮发生了什么事
1.任务A、B和C被提交到队列;
1.所有任务都在运行,而C最早完成。
1.任务B引发异常。

  1. await asyncio.gater()立即返回,print('Got an exception:', e)返回屏幕。
    1.任务A继续运行并打印“==〉〉任务A完成...”
    ∮你的测试有什么问题
    如@deceze所述,程序在捕获异常并返回main()后立即退出,因此任务A和C终止是因为整个进程终止,而不是因为取消。
    要修复此问题,请将await asyncio.sleep(20)添加到main()函数的末尾。
o4hqfura

o4hqfura2#

这里主要问题的答案是使用asyncio.as_complete。将main()函数代码更改为:

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

    # Handle results in the order the task are completed
    # if exeption you can handle that as well. 
    for coroutine in asyncio.as_completed(tasks):
        try:
            results = await coroutine
        except Exception as e:
            print('Got an exception:', e)
        else:
            print('Results:', results)

相关问题