发布
社区首页 >问答首页 >在python asyncio.Task中直到主任务完成后才引发异常

在python asyncio.Task中直到主任务完成后才引发异常
EN

Stack Overflow用户
提问于 2020-12-19 10:54:06
回答 1查看 2.8K关注 0票数 0

这是代码,我认为程序会立即崩溃,因为这是不寻常的例外。然而,当主要任务coro2完成时,它等待了10秒。

代码语言:javascript
代码运行次数:0
复制
import asyncio

@asyncio.coroutine
def coro1():
    print("coro1 primed")
    yield
    raise Exception("abc")

@asyncio.coroutine
def coro2(loop):
    try:
        print("coro2 primed")
        ts = [asyncio.Task(coro1(),loop=loop) for _ in range(2)]
        res = yield from asyncio.sleep(10)
        print(res)
    except Exception as e:
        print(e)
        raise

loop= asyncio.get_event_loop()
loop.run_until_complete(coro2(loop))

我认为这是一个严重的问题,因为在更复杂的程序中,这会使进程永远停滞不前,而不是因为异常信息而崩溃。

此外,我在except块中在run_until_complete源代码中设置了一个断点,但没有触发。我感兴趣的是哪个代码在python异步中处理了该异常。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2020-12-19 13:07:08

首先,没有理由在Python中使用具有多年来可用的异步/等待语法的基于生成器的协同器,而coroutine装饰器现在已不再推荐并计划删除。另外,您不需要将事件循环向下传递到每个协同线,您可以在需要时使用asyncio.get_event_loop()来获得它。但这些和你的问题无关。

except块在coro2中没有触发,因为在coro1中引发的异常没有传播到coro2。这是因为您显式地将coro1作为一个任务运行,它在后台执行它,而不是等待它。您应该始终确保您的任务被等待,然后异常不会被忽略;系统地这样做有时被称为结构化并发

编写以上内容的正确方法如下:

代码语言:javascript
代码运行次数:0
复制
async def coro1():
    print("coro1 primed")
    await asyncio.sleep(0)  # yield to the event loop
    raise Exception("abc")

async def coro2():
    try:
        print("coro2 primed")
        ts = [asyncio.create_task(coro1()) for _ in range(2)]
        await asyncio.sleep(10)
        # ensure we pick up results of the tasks that we've started
        for t in ts:
            await t
        print(res)
    except Exception as e:
        print(e)
        raise

asyncio.run(coro2())

请注意,这将运行sleep()直到完成,然后才会传播由后台任务引发的异常。如果您想立即进行传播,可以使用asyncio.gather(),在这种情况下,您首先不必费心显式地创建任务:

代码语言:javascript
代码运行次数:0
复制
async def coro2():
    try:
        print("coro2 primed")
        res, *ignored = await asyncio.gather(
            asyncio.sleep(10),
            *[(coro1()) for _ in range(2)]
        )
        print(res)
    except Exception as e:
        print(e)
        raise

我感兴趣的是哪个代码在python异步中处理了该异常。

由coroutine引发的未被处理的异常被异步捕获并存储在任务对象中。这允许您等待任务,或者(如果您知道任务已经完成)使用result()方法获得其结果,其中任一方法将传播(重新引发)异常。由于您的代码从未访问任务的结果,因此异常实例在任务对象中仍然被遗忘。Python注意到了这一点,并在任务对象被销毁时打印了一个“任务异常从未检索过”警告,但是这个警告是在尽力而为的基础上提供的,通常来得太晚,不应该依赖。

票数 5
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/65369022

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档