Python协程未按预期0.5秒间隔执行的原因排查
问题:协程任务未按预期间隔执行
我有一个按特定频率触发事件的函数,以及作为动作的协程任务。以下是我的实现(wrapper函数),但协程似乎同时运行,请问这是怎么回事?
import time import asyncio start = time.time() def main(callback): for i in range(5): time.sleep(.5) callback() async def sub_task(): print("sub_task", time.time() - start) #----------------------- # 以下是我的实现 def wrapper(): asyncio.create_task(sub_task()) main(wrapper) # 输出:为什么它们不是按0.5秒间隔执行? #sub_task 2.57704758644104 #sub_task 2.57704758644104 #sub_task 2.57704758644104 #sub_task 2.5780487060546875 #sub_task 2.5780487060546875
注:代码在干净的Jupyter Notebook中测试。
原因分析
核心问题是你混用了同步阻塞代码和异步协程,且没有正确调度事件循环:
main里的time.sleep(.5)是同步阻塞调用,会直接卡住整个线程,这段时间内asyncio的事件循环完全无法运行。asyncio.create_task()只是把协程加入事件循环的任务队列,但此时事件循环未启动,所有任务都处于待执行状态。- 直到
main函数执行完毕(总共阻塞2.5秒),Jupyter后台的事件循环才会接管,一次性跑完所有待执行的sub_task,所以所有输出时间几乎一致。
解决方案
方案1:同步环境中手动调度事件循环
如果必须保留main同步函数,可以在每次创建任务后,手动让事件循环执行该协程:
def wrapper(): loop = asyncio.get_event_loop() loop.run_until_complete(sub_task())
这样每次调用wrapper时,会同步等待sub_task执行完成,输出会按0.5秒间隔出现。
方案2:完全改用异步代码(推荐)
把同步的main改成异步函数,用asyncio.sleep()替代time.sleep(),让事件循环正常调度任务:
import time import asyncio start = time.time() async def main(): for i in range(5): await asyncio.sleep(.5) asyncio.create_task(sub_task()) async def sub_task(): print("sub_task", time.time() - start) asyncio.run(main())
asyncio.sleep()不会阻塞事件循环,每次等待0.5秒后创建的任务会被及时调度,输出时间会符合预期间隔。
内容的提问来源于stack exchange,提问作者qing6010
相关产品推荐
相关产品推荐

