为何Python中await long_function无法实现协程异步执行?附示例
为什么
await long_function()无法触发协程切换? 要搞懂这个问题,得从Python asyncio的协作式多任务核心逻辑说起:
1. 协程切换的前提:主动让出控制权
asyncio的事件循环是协作式的,和操作系统的抢占式多线程不同——协程不会被强制打断,必须自己主动“举手”告诉事件循环:“我现在可以暂停,你去跑别的协程吧”。
只有当await的对象是一个能触发事件循环调度的可等待对象时,才会发生协程切换。这类对象包括:
asyncio.Task/asyncio.Future:代表异步任务- 内置的异步IO操作(比如
asyncio.sleep()、异步网络请求) - 自定义的实现了
__await__方法且主动让出控制权的对象
2. 你的long_function只是个“伪异步”函数
你写的long_function虽然用async定义,但里面是纯CPU密集的同步循环:
async def long_function(): for _ in range(50000000): pass
它没有任何IO操作,也没有调用任何会让事件循环接管的API。当你await long_function()时,事件循环会执行这个函数的所有代码——因为这个函数没有任何“暂停点”,它会一直占用单线程,直到把5000万次循环跑完,才会回到事件循环,这时才会切换到另一个count协程。
这就是为什么你看到的是第一个count打印完0-9,才轮到第二个count执行。
3. asyncio.sleep(0)的作用:主动触发调度
asyncio.sleep(0)虽然不会让程序等待实际时间,但它是asyncio提供的标准可等待对象。当你await它时,它会主动向事件循环发出信号:“我暂时不需要执行,你去调度其他就绪的协程吧”。
这时事件循环会把当前count协程挂起,转而运行另一个等待执行的count协程,于是就出现了你预期的交替输出:0、0、1、1...
总结
- 不是所有
await都会触发协程切换,只有当await的对象主动让出控制权时才会发生。 - 纯CPU密集的
async函数不会触发切换,因为它没有给事件循环调度的机会。 asyncio.sleep(0)是一种手动触发协程切换的技巧,适用于需要在CPU密集型代码中主动让出控制权的场景。
内容的提问来源于stack exchange,提问作者Jack Scandall
相关产品推荐
相关产品推荐

