Python如何区分异步函数调用与实际I/O操作?机制解析
Python异步await的控制权流转机制
先澄清一个常见误解:await并非对不同对象做“同步调用/交还控制权”的二元区分——核心逻辑要看被await的可等待对象(Awaitable)是否会触发协程暂停。
核心基础:可等待对象的三类类型
Python中,await只能作用于实现了__await__()方法的对象,这类对象主要分为三类:
- 协程对象:由
async def定义的函数调用后生成的实例 - 任务对象(Task):由
asyncio.create_task()等方法包装协程生成的对象,可被事件调度 - 未来对象(Future):底层表示异步操作结果的对象,所有异步I/O操作最终都会关联到它
为什么await bar()看起来没交还控制权?
当你await bar()时,bar()是一个普通协程对象。Python会直接执行这个协程的代码,直到它遇到自己的await语句(也就是await io_function())。
在bar()执行到暂停点之前,它只是一段顺序执行的代码片段,没有主动触发暂停的逻辑——协程本身不会随便交出控制权,只有当它等待某个需要异步等待的操作时,才会触发暂停。
io_function()的特殊之处:绑定了Future对象
像asyncio.sleep()、异步网络请求这类I/O函数,它们内部会创建并返回一个Future对象(或包装了Future的Task)。
当await遇到Future对象时,事件循环会做两件事:
- 检查这个Future是否已完成(比如I/O操作是否拿到结果)
- 如果未完成,当前协程会立即暂停,把控制权交还给事件循环,让事件循环去执行其他就绪的协程
- 当Future标记为完成(I/O操作结束),事件循环会唤醒对应的协程,让它继续执行后续代码
你的代码执行流程拆解
async def foo(): await bar() # 执行bar协程,直到bar遇到自身的await async def bar(): await io_function() # 这里io_function返回未完成的Future,触发暂停
- 事件循环启动并执行
foo()协程 foo()执行await bar(),开始顺次执行bar()的代码bar()执行到await io_function(),io_function()返回一个未完成的Future- 此时
bar()暂停,控制权回到事件循环,事件循环可以调度其他协程 - 当
io_function()的I/O操作完成,Future被标记为已完成,事件循环唤醒bar(),bar()执行完毕后返回,foo()继续执行后续逻辑
总结:没有特殊标记,只看对象行为
不存在专门标识“这是I/O”的内部标记,核心是被await的对象是否会给事件循环接管控制权的机会:
- 普通协程(无内部暂停逻辑):
await会直接执行完整个协程,期间不会交出控制权 - 绑定异步I/O的Future/Task:
await会触发协程暂停,把控制权交还给事件循环
内容的提问来源于stack exchange,提问作者Varun Gawande
相关产品推荐
相关产品推荐

