python协程调度由事件循环控制,非代码顺序执行,取决于await挂起点、任务提交时机、i/o完成顺序及轮询机制;await是调度分界点,无await则同步执行到底。

Python 协程的调度顺序由事件循环(event loop)控制,不是按代码书写顺序“自动”执行,而是取决于 await 的挂起点、任务提交时机、I/O 完成顺序以及事件循环的轮询机制。
await 是调度的分界点
协程只有在遇到 await 表达式时才可能让出控制权。如果协程内部没有 await(比如纯计算或未 await 的协程对象),它会同步执行到底,不会被调度中断。
-
错误写法:
async def f(): return 42—— 直接返回,不挂起,不参与调度 -
正确挂起:
await asyncio.sleep(0)或await asyncio.to_thread(...)等,显式交还控制权 - 即使
await后面是已完成的 Future(如await asyncio.ensure_future(...)已完成),也会立即继续,但仍是调度器的一次检查点
任务创建时机影响执行起点
使用 asyncio.create_task()、asyncio.ensure_future() 或直接 await 都会影响协程何时进入就绪队列。
-
await coro():当前协程阻塞,直到该协程返回;它不并发,是串行等待 -
asyncio.create_task(coro()):立即把协程包装为 Task 并加入事件循环的就绪队列(若尚未运行),后续由循环统一调度 - 多个
create_task调用本身是同步的,但它们对应的协程不一定按创建顺序开始执行——取决于首个await出现的位置和 I/O 响应时间
事件循环按“就绪优先”而非“创建优先”调度
事件循环维护一个就绪队列(ready queue),其中的任务是已解除挂起(例如 I/O 完成、sleep 到时、被 asyncio.wakeup() 唤醒)的协程。
立即学习“Python免费学习笔记(深入)”;
- 刚创建的 Task 若还没首次
await,它处于 pending 状态,不会立刻入就绪队列 - 一旦某个 Task 首次
await并挂起后又被唤醒(如 socket 可读),它才会被推入就绪队列 - 就绪队列通常是 FIFO,但某些事件循环实现(如 uvloop)可能有优化,且高优先级回调(如
call_soon())会插队到队首
常见误解与验证方式
容易误以为 “先 create_task 就先运行”,其实关键看谁先 ready。可通过日志 + asyncio.current_task() 或 asyncio.all_tasks() 观察实际状态流转。
- 加
print(f"start {name}")在协程开头,再加await asyncio.sleep(0)模拟让渡,可清晰看到调度穿插 - 用
asyncio.run()启动时,主协程默认是第一个 Task;它第一次await后,其他已就绪的 Task 才有机会执行 - 长时间 CPU 密集操作(如
sum(range(10**7)))会阻塞整个事件循环;必须用asyncio.to_thread()或loop.run_in_executor()卸载










