为何asyncio.sleep(0)无法让出Python事件循环控制权?
问题分析:asyncio.sleep(0)为何未让出控制权
核心原因是asyncio.sleep(0)和asyncio.sleep(0.0001)的调度逻辑存在差异,再结合你代码里的同步阻塞调用,最终导致了不同的执行顺序。
先看代码里的致命阻塞点
你在heavy_job里用了time.sleep(3)——这是同步阻塞调用,会直接卡住事件循环所在的整个线程,期间事件循环完全无法处理任何其他任务,包括light_job中await asyncio.sleep(1)到期后的唤醒操作。
为什么asyncio.sleep(0)没起作用?
当heavy_job执行到await asyncio.sleep(0)时:
asyncio.sleep(0)的底层实现是通过loop.call_soon,把当前协程重新放到当前就绪队列的末尾。- 事件循环会先处理这个
call_soon回调,把heavy_job放回就绪队列,之后才会去检查所有到期的定时器(此时light_job的sleep(1)早就到期了),把light_job加入就绪队列。 - 这时就绪队列的顺序是
heavy_job在前、light_job在后,事件循环按队列顺序执行,所以heavy_job会先继续完成剩余的time.sleep(3),直到完全结束才会轮到light_job。
为什么asyncio.sleep(0.0001)能正常工作?
换成await asyncio.sleep(0.0001)时:
- 这会创建一个短延迟的定时器,事件循环会优先检查所有已经到期的定时器(包括
light_job的sleep(1)),把light_job加入就绪队列。 - 此时就绪队列里只有
light_job,事件循环就先执行它,等light_job完成后,再回来处理heavy_job的sleep(0.0001)定时器,继续执行heavy_job的剩余部分。
额外提醒:asyncio程序的正确写法
你代码里的time.sleep(3)是典型的错误用法,在asyncio异步程序中,所有阻塞操作都应该使用异步版本。修改后的代码示例:
import asyncio import time loop = asyncio.get_event_loop() async def light_job(): print("hello ") print(time.time()) await asyncio.sleep(1) print(time.time()) print("world!") async def heavy_job(): print("heavy start") await asyncio.sleep(3) # 替换为异步sleep print("heavy halt started") await asyncio.sleep(0) print("heavy halt ended") await asyncio.sleep(3) # 替换为异步sleep print("heavy done") loop.run_until_complete(asyncio.gather( light_job(), heavy_job() ))
这样修改后,事件循环在等待期间可以正常调度其他协程,light_job会在1秒后正常唤醒,完全不需要依赖heavy_job的sleep操作来让出控制权。
内容的提问来源于stack exchange,提问作者Saeed Mofidi
相关产品推荐
相关产品推荐

