asyncio - 从任务中重新引发异常

时间:2021-02-20 15:51:04

标签: python python-asyncio

我正在使用 asyncio 进行一些 TCP 通信。我有一个 Receive() 函数,它在无限循环中执行 read()。这作为使用 asyncio.create_task(Receive()) 的后台任务运行。

现在,如果连接被对等方关闭,则会引发异常(或可能是任何其他异常),我会在 Receive() 函数中捕获该异常。但是,我想重新提出该异常,以便外部代码可以决定要做什么(例如重新连接)。

由于在任务中引发了异常,我不知道如何检索它。

我试图创建一个例子来说明我的意思:

import asyncio

async def divide(x):
    try:
        return 1/x
    except Exception as e:
        print("Divide inner exception: ", e)
        raise   # Re-raise so main() can handle it

async def someFn():
    asyncio.create_task(divide(0))  # Exception is never retrieved
    # await divide(0) # This will raise two exceptions - the original in divide() and in main()

async def main():
    try:
        await someFn()
        # Do other things while someFn() runs
    except Exception as e:
        print("main exception: ", e)

asyncio.run(main())

如何在 main() 中获取任务异常?

2 个答案:

答案 0 :(得分:2)

<块引用>

如何在 main() 中获取任务异常?

您可以利用引发异常的任务完成这一事实,而不是让任务在后台运行,而是实际等待其完成。这要求创建任务的代码不保留 create_task() 返回的任务对象,而是将其返回给调用者或将其存储到共享任务集。 (诸如 trio 之类的库甚至不允许盲目地生成后台任务,而是要求每个任务都伴随着一个 nursery 来定义谁来处理其异常。

例如:

async def someFn():
    # set up a background task, but also return it to the caller
    t = asyncio.create_task(divide(0))
    return t

async def other_things():
    await asyncio.sleep(1)

async def main():
    try:
        task = await someFn()
        # await both the background task and other things, immediately
        # propagating an exception in either
        await asyncio.gather(task, other_things())
    except Exception as e:
        print("main exception: ", e)

答案 1 :(得分:0)

另一种选择是使用 add_done_callback,这提供了一定的灵活性,无需事先知道代码中处理任务的位置(如果使用 asyncio.gather() 选项,则需要这样做)。

import asyncio

async def divide(x):
    try:
        return 1/x
    except Exception as e:
        print("Divide inner exception: ", e)
        raise   # Re-raise so callback can handle it 
                # (or don't handle exception here and allow callback to manage)

async def someFn():
    task = asyncio.create_task(divide(0))
    task.add_done_callback(task_cb)

def task_cb(task):
    try:
        task.result()
    except asyncio.CancelledError:
        pass  # Task cancellation should not be logged as an error.
    except Exception as e:
        print('Exception raised by task: ', e)

async def main():
    try:
        await someFn()
        # Do other things while someFn() runs
    except Exception as e:
        # Handle the exception in some way
        print("main exception: ", e)

asyncio.run(main())
相关问题