Python中所有async/await表达式都具备异步性吗?
为什么片段2和同步代码表现一致?
片段2中,await one()会直接暂停main协程,直到one()完全执行完毕才会继续执行await two()。也就是说,one()在sleep 3秒的过程中,事件循环里只有one这一个正在运行的任务,没有其他可调度的协程,只能等着one结束后再启动two。这种串行等待的逻辑和同步代码里先调用one()再调用two()完全一致,所以耗时和输出顺序自然没有区别。
asyncio的非阻塞性怎么体现?
asyncio的非阻塞不是“用了async/await就自动生效”,核心在于事件循环的调度逻辑:当一个协程遇到可等待的IO操作(比如asyncio.sleep、异步网络请求)时,它会主动把控制权交还给事件循环,此时事件循环可以去执行其他已经被调度好的协程(比如用create_task创建的任务)。
比如片段1里,asyncio.create_task(two())把two()包装成任务立刻提交给事件循环。当one()执行到await asyncio.sleep(3)时,它让出控制权,事件循环就会去运行two()任务,所以two()先完成输出,等3秒后one()再继续执行。
如果要让片段2体现非阻塞,只需要把两个协程并发调度,比如用asyncio.gather:
async def main(): # 同时调度one和two,等待两者完成 await asyncio.gather(one(), two()) asyncio.run(main())
这个版本的输出会是:
two done one done
总耗时约3秒(而非同步的3.1秒),因为one()和two()的sleep操作是并行进行的,这就是非阻塞性的直观体现。
不使用create_task、run_in_executor时,asyncio还有优势吗?
有,但要结合场景来看:
- 异步IO库适配:如果代码依赖异步IO驱动(比如aiohttp、asyncpg),即使是顺序await,也比同步代码高效。同步IO会阻塞整个线程,而asyncio的await只是让出控制权,事件循环可以在等待IO的间隙处理其他后台任务(比如定时器、信号处理)。
- 代码扩展性:用async/await编写的代码,后续如果需要改成并发,只需要调整调度方式(比如添加
create_task或gather);而同步代码要改成并发则需要重构为线程/进程模型,改动成本更高。 - 单线程高IO场景:处理大量IO-bound任务时,asyncio的单线程模型比线程池开销更小——因为没有线程切换的额外成本,但前提是要把这些任务并发调度起来。
当然,如果只是像片段2那样,顺序执行两个无实际IO的协程(或纯sleep),那确实和同步代码没有区别,因为事件循环没有机会调度其他任务。
内容的提问来源于stack exchange,提问作者mcdominik

