如何在Python中按复杂依赖关系高效并发运行协程以最小化总耗时?
如何在Python中按复杂依赖关系高效并发运行协程以最小化总耗时?
这个场景我太熟了——要在满足依赖的前提下把并发效率拉满,核心就是让每个任务一满足前置条件就立刻启动,别让它被无关的任务拖着。针对你说的A、B、C、D的依赖关系,我们可以用asyncio.create_task来精准控制任务的启动时机,完全不需要用到容易搞乱的回调。
最优实现思路
- 先同时启动A和B的任务,让它们并行执行,互不等待
- 一旦A的结果出来,立刻启动C(不用等B完成)
- 等B的结果出来后,因为A已经完成,直接启动D
- 最后只需要等C和D都执行完毕即可
这样所有能并行的环节都不会浪费时间:A和B并行,C在A结束后立刻启动,D在A+B都结束后立刻启动,C和D也能并行运行,总耗时肯定是最小的。
完整代码示例
import asyncio async def get_a(): # 模拟A的耗时,比如2秒 await asyncio.sleep(2) return "Result of A" async def get_b(): # 模拟B的耗时,比如3秒 await asyncio.sleep(3) return "Result of B" async def get_c(a_result): # 模拟C的耗时,比如1秒 await asyncio.sleep(1) return f"Result of C (depends on {a_result})" async def get_d(a_result, b_result): # 模拟D的耗时,比如1秒 await asyncio.sleep(1) return f"Result of D (depends on {a_result} and {b_result})" async def main(): # 1. 同时启动A和B的任务,让它们并行跑 task_a = asyncio.create_task(get_a()) task_b = asyncio.create_task(get_b()) # 2. 等A完成,拿到结果后立刻启动C a = await task_a task_c = asyncio.create_task(get_c(a)) # 3. 等B完成,此时A已经完了,直接启动D b = await task_b task_d = asyncio.create_task(get_d(a, b)) # 4. 最后等C和D都完成,拿到结果 c, d = await asyncio.gather(task_c, task_d) print(c) print(d) if __name__ == "__main__": asyncio.run(main())
为什么这个方案比你提到的两种更好?
- 对比第一种用
asyncio.gather(get_a(), get_b())的方式:这种方式要等A和B都完成才会继续,等于C要白白等B跑完的时间,完全浪费了并发空间 - 对比第二种把A和C绑成一组和B并行的方式:这种方式下,D必须等C和B都完成才能启动——如果C比B慢很多,那D就要等C跑完才能开始,而实际上D只需要A和B的结果,完全不用等C
扩展到更复杂的依赖链
如果你的实际场景有更多任务、更复杂的依赖(比如E要等C和D,F要等B和E),这个思路也完全适用:
- 每个任务都用
create_task在满足依赖后立刻启动 - 用
await等待单个任务的结果,用来触发依赖它的任务 - 最后用
gather等待所有需要最终结果的任务完成
这种方式逻辑清晰,比回调好维护太多,每个环节的依赖关系一目了然。
备注:内容来源于stack exchange,提问作者Pwnosaurus
相关产品推荐
相关产品推荐

