Python 3中如何让asyncio按顺序执行函数?实例解析
咱们先搞清楚为啥会出现乱序的情况,再看怎么解决~
为啥会出现无序输出?
核心原因就是多线程的并发调度特性:当你用ThreadPoolExecutor设置了多个工作线程时,提交的任务会被分配到不同线程中同时执行,而线程的执行顺序完全由操作系统的调度器决定。
哪怕你严格按0到9的顺序提交任务,不同线程的执行速度可能因为CPU时间片分配、系统负载等细微差异,导致后提交的任务反而先完成。比如线程池只有4个线程时,前4个任务(0-3)同时启动,当某个线程完成后会立刻取下一个任务(比如4),如果任务9所在的线程比任务8的线程更早结束,那9就会比8先被打印出来,自然就乱序了。
另外,如果你的代码是一次性把所有任务都扔给事件循环并发执行(比如用asyncio.create_task批量创建任务),而不是等前一个任务做完再提交下一个,这种并发执行的方式会让输出顺序的不确定性更明显。
怎么确保始终按0-9顺序输出?
这里有两种实用方案,你可以根据自己的需求选:
方案1:保留并发,统一按顺序输出
这种方法既保留了多线程并发执行的效率(所有任务同时跑,节省时间),又能保证输出顺序正确。思路是让任务只负责返回结果,不直接打印,等所有任务都完成后,再按提交顺序统一输出:
import asyncio from concurrent.futures import ThreadPoolExecutor async def get_task_result(num, pool): loop = asyncio.get_running_loop() # 线程池里执行任务,只返回结果不打印 return await loop.run_in_executor(pool, lambda: num) async def main(): with ThreadPoolExecutor(max_workers=4) as executor: # 先把所有任务都提交,得到一组future对象 task_futures = [get_task_result(i, executor) for i in range(10)] # 用asyncio.gather等待所有任务完成,它会严格按提交顺序返回结果 results = await asyncio.gather(*task_futures) # 现在按顺序打印就行啦 for num in results: print(num) asyncio.run(main())
方案2:串行执行任务(逐个等待)
如果你不在乎并发的效率,只需要简单粗暴保证顺序,那可以改成等前一个任务完全完成后,再提交下一个任务:
import asyncio from concurrent.futures import ThreadPoolExecutor async def print_single_num(num, pool): loop = asyncio.get_running_loop() await loop.run_in_executor(pool, print, num) async def main(): with ThreadPoolExecutor(max_workers=4) as executor: # 一个一个来,等当前任务打印完再搞下一个 for i in range(10): await print_single_num(i, executor) asyncio.run(main())
这种方式下,每个任务必须等前一个任务打印结束才会启动,所以输出顺序肯定是0到9,但缺点是多线程的并发优势就没了,任务相当于串行执行,速度会慢一些。
内容的提问来源于stack exchange,提问作者Nyxynyx
相关产品推荐
相关产品推荐

