首页 > 解决方案 > 从带有异常的异步任务列表中收集结果

问题描述

编码...

import asyncio
import random
from time import perf_counter
from typing import Iterable
from pprint import pprint

async def coro(n, i, threshold=0.4):
    await asyncio.sleep(i)
    if i > threshold:
        # For illustration's sake - some coroutines may raise,
        # and we want to accomodate that and just test for exception
        # instances in the results of asyncio.gather(return_exceptions=True)
        raise Exception(f"{i} of Task-{n} is too high")
    return i

async def main(it: Iterable, timeout: float) -> tuple:
    tasks = [asyncio.create_task(coro(i+1, d), name=f"Task-{i+1}") for i, d in enumerate(it)]
    await asyncio.wait(tasks, timeout=timeout)
    return tasks  # *not* (done, pending)

timeout = 0.5
random.seed(444)
n = 10
it = [random.random() for _ in range(n)]
start = perf_counter()
tasks = asyncio.run(main(it=it, timeout=timeout))
elapsed = perf_counter() - start
print(f"Done main({n}) in {elapsed:0.2f} seconds\n")
pprint(tasks)
print('----')

# does not work from here on....

res = []
for t in tasks:
    try:
        r = t.result() # gives an error!!!
    except Exception as e:
        res.append(e)
    else:
        res.append(r)
pprint(res)

...不适用于收集任务结果。它失败了......

Traceback (most recent call last):
  File "c:\Users\user\Documents\user\projects\learn\asyncio\wrap_gather_in_timeout.py", line 8, in coro
    await asyncio.sleep(i)
  File "C:\Users\user\AppData\Local\Programs\Python\Python39\lib\asyncio\tasks.py", line 654, in sleep
    return await future
asyncio.exceptions.CancelledError

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "c:\Users\user\Documents\user\projects\learn\asyncio\wrap_gather_in_timeout.py", line 35, in <module>
    r = t.result()
asyncio.exceptions.CancelledError
Task exception was never retrieved
future: <Task finished name='Task-7' coro=<coro() done, defined at c:\Users\user\Documents\user\projects\learn\asyncio\wrap_gather_in_timeout.py:7> exception=Exception('i too high')>
Traceback (most recent call last):
  File "c:\Users\user\Documents\user\projects\learn\asyncio\wrap_gather_in_timeout.py", line 13, in coro
    raise Exception("i too high")
Exception: i too high

代码在 python 3.9 中运行。知道我哪里出错了,为什么?

是不是因为任务抛出异常后需要取消?我无法成功实施它。

灵感来源:包装 asyncio.gather SO 的解决方案

标签: pythonpython-asyncio

解决方案


您的代码有效,您无法res成功创建的问题是因为代码不只引发普通Exception类。由于任务失败,它最终会调用asyncio.exceptions.CancelledErrorwhich 如果我们查看文档继承自BaseExceptionnot Exception。此更改在 Python 3.8 中是新的,并且由于您使用的是 Python 3.9,因此该更改是实时的。将您的代码稍微更改为以下结果:

res = []
for t in tasks:
    try:
        r = t.result() # gives an error!!!
    except BaseException as e:
        res.append(e)
        continue
    res.append(r)

print(res)
[0.3088946587429545,
 0.01323751590501987,
 Exception('0.4844375347808497 of Task-3 is too high'),
 asyncio.exceptions.CancelledError(),
 asyncio.exceptions.CancelledError(),
 asyncio.exceptions.CancelledError(),
 Exception('0.4419557492849159 of Task-7 is too high'),
 0.3113884366691503,
 0.07422124156714727,
 asyncio.exceptions.CancelledError()]

推荐阅读