为什么await第二个asyncio任务时,未被await的第一个任务仍会运行
问题
以下是基于Python 3.10编写的异步代码:
import asyncio import time async def say_after(delay, what): await asyncio.sleep(delay) print(what) async def main(): t1 = asyncio.create_task(say_after(1, 'task 1')) t2 = asyncio.create_task(say_after(2, 'task 2')) print(f'Start at {time.strftime("%X")}') await t2 print(f'End at {time.strftime("%X")}') asyncio.run(main(), debug=True)
代码运行后输出如下:
Start at 03:32:50 task 1 task 2 End at 03:32:52
代码中仅对t2执行了await操作,并未显式await t1,但t1仍然正常运行完成,该代码修改自Python官方文档asyncio-task章节的示例,该现象出现的原因是什么?
原因说明
- 先搞懂
asyncio.create_task()的基本逻辑:只要你调用了这个方法,传入的协程会立刻被包装成Task,丢到当前运行的事件循环的待调度队列里。事件循环处于运行状态时,队列里所有Task都会按调度规则自动执行,根本不需要你挨个await才会启动。await某个Task的作用只是把当前协程挂起,等这个Task跑完再往下走,它从来不是启动Task的开关。 - 这段代码里执行
await t2时,main协程会挂起整整2秒等待t2结束。这2秒的挂起窗口内事件循环始终在正常运行,会轮询处理所有已注册、到达执行时机的Task:t1只需要等待1秒就能执行,自然会在这个窗口内被调度运行,打印出task 1,完全不需要单独对它执行await。 - 这个运行结果存在巧合性:如果把t2的等待时间改成小于1秒(比如0.5秒),那
await t2只会让main协程挂起0.5秒就恢复执行,等main协程运行到结束时,asyncio.run()会直接取消所有还没执行完成的后台Task,这时候t1还没等够1秒就会被强制终止,无法完整执行。当前示例里await t2等待的2秒刚好长于t1需要的1秒执行时间,给够了t1运行窗口,才会出现没显式await也正常跑完的效果。
内容的提问来源于stack exchange,提问作者De Scar
相关产品推荐
相关产品推荐

