Python 异步睡眠问题执行顺序

问题描述 投票:0回答:1

您好,我在学习一些关于 asyncio 的教程时有一个问题,如下所示:

import asyncio
async def task_coroutine():
    print('start executing task')
    await asyncio.sleep(1.0)
    print('done waiting in task')
    raise Exception('error in task')
    return 99

async def main():
    print('main started')
    task = asyncio.create_task(task_coroutine())
    await asyncio.sleep(1.1)
    print('done waiting in main')
    try:
        value = task.result()
    except Exception as e:
        print(e)
    print('end of main')
asyncio.run(main())

正如我所料,结果是

main started
start executing task
done waiting in task
done waiting in main
error in task
end of main

因为当任务挂起时,主程序仍在休眠,它会切换回任务来抛出。

我尝试了一下,将 main 的睡眠时间更改为 0.1(之前是 1.1),结果也如我所料

main started
start executing task
done waiting in main
Result is not set.
end of main

由于主要尝试在任务完成之前收集任务结果,因此它会抛出“结果未设置”

但是,当我将 main 的睡眠时间设置为 1.0000000001 时,它给出:

main started
start executing task
done waiting in main
Result is not set.
end of main
done waiting in task

Task exception was never retrieved
future: <Task finished name='Task-2' coro=<task_coroutine() done, defined at /Users/user/Library/Application Support/JetBrains/PyCharm2024.1/scratches/scratch.py:3> exception=Exception('error in task')>
Traceback (most recent call last):
  File "/Users/charlielao/Library/Application Support/JetBrains/PyCharm2024.1/scratches/scratch.py", line 7, in task_coroutine
    raise Exception('error in task')
Exception: error in task

我不太明白为什么在 main 完成后它会在最后打印“done waiting in task”,而在 0.1 情况下却没有,为什么在这种情况下会出现明确的错误,事件的确切顺序是什么?

python async-await python-asyncio
1个回答
0
投票

总而言之,

asyncio.run()
main()
协程完成后继续运行任务。这是为了给其他任务一个优雅终止的机会,而不是留在某种未知的状态。由于睡眠之间的持续时间差异非常小,这意味着子任务中的等待语句正常完成。因此,子任务可以通过引发自定义异常来完成。

详情

当函数

asyncio.run()
完成运行它所传递的协程时,它会执行一些后期清理步骤。其中一个步骤是对每个待处理任务调用
cancel()
,然后运行事件循环,直到每个任务完成。当任务被取消时,会在任务的下一个待处理
CancelledError
语句处引发
await
。这会导致表现良好的任务快速终止。

时间

睡眠持续时间的差异非常小,因此当您调用

sleep()
task.result()
仍处于待处理状态,但在事件循环开始取消待处理任务时,它就已完成。这在实践中意味着事件循环在有机会取消睡眠协程之前会处理发出睡眠协程结束信号的事件。由于任务中没有进一步的
await
陈述,因此没有机会提出
CancelledError
。相反,您的任务将继续执行,直到到达您的
raise
语句。此语句会导致您的任务因异常而终止。如果任何任务因非
CancelledError
的异常而终止,则被视为错误,并且永远不会检索到该异常。因此,事件循环会报告此未处理的异常。

© www.soinside.com 2019 - 2024. All rights reserved.