使用asyncio创建任务为何总耗时10秒而非最长任务的4秒?
问题:asyncio异步任务总耗时不符合预期
我用asyncio编写了异步任务,预期总耗时为最长任务的4秒,但实际总耗时是10秒(0+1+2+3+4),请问原因是什么?
代码如下:
import asyncio,time async def say_after(delay, what): await asyncio.sleep(delay) print(f"what = {what}, at {time.strftime('%X')}") background_tasks = set() async def main(): for i in range(5): task = asyncio.create_task(say_after(delay=i,what=i)) # Add task to the set. This creates a strong reference. background_tasks.add(task) await task # To prevent keeping references to finished tasks forever, # make each task remove its own reference from the set after # completion: task.add_done_callback(background_tasks.discard) # discard is a set method. if __name__=="__main__": asyncio.run(main())
执行结果:
备注:我是参考Python官方文档编写的代码。
原因分析与解决办法
核心原因
循环中创建异步任务后立刻执行await task,会强制让main函数暂停,等待当前异步任务完全执行完毕才进入下一次循环。原本的异步并行逻辑被强行改成了串行执行,总耗时因此变成所有任务延迟时间的总和(0+1+2+3+4=10秒),而非并行时的最长延迟4秒。
官方文档用集合保存后台任务的示例,目的是让任务在后台并行运行,并非创建一个就等待一个。你错误地在循环内加入await task,破坏了异步并行的逻辑。
修正方案
把await task从循环内移出,等所有异步任务创建完成后,统一等待所有任务执行完毕。修改后的main函数如下:
async def main(): for i in range(5): task = asyncio.create_task(say_after(delay=i,what=i)) background_tasks.add(task) # 提前注册回调,避免任务完成后未清理内存引用 task.add_done_callback(background_tasks.discard) # 等待所有后台任务执行完成 await asyncio.gather(*background_tasks)
修改后所有任务会并行启动,总耗时约为4秒(最长任务的延迟时间),符合预期。
内容的提问来源于stack exchange,提问作者An old man in the sea.
相关产品推荐
相关产品推荐

