Python Async函数执行顺序判定逻辑及代码执行疑问解析
解析你的asyncio代码执行顺序问题
先看你运行的代码:
from asyncio import create_task, sleep, run async def print_(delay, x): print(f'start: {x}') await sleep(delay) print(f'end: {x}') async def main(): slow_task = create_task(print_(2, 'slow task')) fast_task = create_task(print_(1, 'fast task')) # 此处执行顺序不符合预期: print(0) await slow_task print(1) await fast_task run(main())
得到的输出:
0 start: slow task start: fast task end: fast task end: slow task 1
具体发生了什么?
我们一步步拆解执行流程:
- 调用
run(main())启动事件循环,开始执行main协程。 create_task会把传入的协程包装成任务,立即加入事件循环的待执行队列,但此时不会立刻执行——因为当前main协程还在同步执行阶段,没有让出控制权。- 执行
print(0),输出0。 - 遇到
await slow_task:此时main协程被挂起,控制权交还给事件循环,事件循环开始处理队列里的任务:- 先调度
slow_task对应的print_(2, 'slow task'):输出start: slow task,然后遇到await sleep(2),这个协程被挂起(等待2秒),事件循环继续找下一个可执行任务。 - 接着调度
fast_task对应的print_(1, 'fast task'):输出start: fast task,遇到await sleep(1),协程被挂起(等待1秒)。
- 先调度
- 事件循环进入等待状态,直到有任务的等待条件满足:
- 1秒后,
fast_task的sleep(1)完成,事件循环唤醒它,输出end: fast task,fast_task执行完毕。 - 再过1秒,
slow_task的sleep(2)完成,事件循环唤醒它,输出end: slow task,slow_task执行完毕。
- 1秒后,
- 此时
await slow_task的等待完成,事件循环恢复main协程的执行,执行print(1),输出1。 - 最后遇到
await fast_task,但fast_task已经完成,直接跳过,main协程结束,事件循环终止。
如何预测执行顺序?
你困惑的print(1)为什么在所有任务完成后才执行,核心原因是:await slow_task会一直等待slow_task完全执行完毕,才会继续执行main协程里的后续代码。而在等待slow_task的过程中,事件循环不会闲着——它会把队列里其他已就绪的任务(比如fast_task)全部执行完,直到slow_task完成。这就导致print(1)要等到两个任务都结束后才会运行。
你原本预期print(1)在任务完成前执行,是对await的作用理解有偏差:await不是“触发任务执行后立刻继续当前代码”,而是“暂停当前协程,等待目标任务完成,期间事件循环会处理其他任务”。
核心规则总结
create_task仅将协程加入事件循环队列,不会立即执行,必须等当前协程让出控制权(比如遇到await)才会调度。await会挂起当前协程,事件循环会优先处理所有已就绪的任务,直到被await的任务完成,才会恢复当前协程。- asyncio采用协作式调度:任务不会被抢占,只有当任务主动让出控制权(比如
await操作),事件循环才会切换到其他任务。
这是标准行为吗?会变化吗?
这是asyncio的标准行为,不会随意变化。
不同的事件循环实现(比如默认的SelectorEventLoop或第三方的uvloop)在调度的细节上可能有微小差异,但核心逻辑完全一致:协作式调度,等待任务期间会处理其他就绪任务。只有当asyncio的核心调度规则发生变更时,行为才会变化,但这种情况极少发生。
事件循环与代码的协同机制
asyncio的事件循环是单线程下协程调度的核心,工作流程如下:
- 启动:调用
run()时,会创建一个事件循环,并将传入的协程作为“主协程”加入调度队列。 - 执行协程:事件循环取出队列中的协程,开始执行,直到协程遇到
await或结束。 - 挂起与调度:当协程遇到
await时,会将自身挂起,把控制权交还给事件循环。事件循环会检查所有已注册的可等待对象(任务、sleep、future等),找出那些已经就绪的(比如sleep时间到、任务完成),然后调度它们执行。 - 恢复执行:当被
await的对象完成时,事件循环会将对应的协程重新加入可执行队列,等待下一次调度。 - 终止:当主协程执行完毕,事件循环会清理所有剩余任务,然后终止。
简单来说,事件循环就像一个“任务调度器”,所有协程的执行、挂起、恢复都由它控制,而协程通过await主动让出控制权,实现多任务的协作式并发。
内容的提问来源于stack exchange,提问作者Petko.Maria.John
相关产品推荐
相关产品推荐

