Python Asyncio:如何向运行中的事件循环添加新协程?代码问题排查
回答
当然可以实现!你的代码里有两个关键问题导致了当前的阻塞行为:
1. 使用阻塞的time.sleep()而非异步休眠
time.sleep()是同步阻塞函数,一旦调用它,整个事件循环会被彻底卡住,直到休眠结束。这意味着其他协程根本没有机会被调度执行,所以只有第一个协程一直在输出。
在asyncio中,必须使用await asyncio.sleep(waiting_time)来实现异步休眠,这样事件循环可以在休眠期间切换到其他协程运行。
2. 错误地await了create_task()的返回值
loop.create_task()会立即创建一个异步任务并将其加入事件循环,但你在循环里使用await loop.create_task(...),这会强制等待第一个任务完成才会创建下一个任务。而你的secondCoro是无限循环,所以第一个任务永远不会结束,后面的9个协程根本没机会被创建。
修复后的代码
下面是调整后的代码,解决了以上两个问题:
import asyncio import random async def secondCoro(myId): while 1: waiting_time = random.randint(1,5) print(f"i am {myId} ") await asyncio.sleep(waiting_time) # 使用异步休眠 async def main(): tasks = [] for i in range(10): task = asyncio.create_task(secondCoro(i)) # 创建任务但不立即等待 tasks.append(task) await asyncio.sleep(0.1) # 这里用异步sleep避免阻塞 # 等待所有任务完成(因为是无限循环,这里实际会一直运行) await asyncio.gather(*tasks) # Python 3.7+推荐使用asyncio.run()替代get_event_loop() asyncio.run(main())
运行这段代码,你会看到10个协程交替输出,完全符合你的预期。
其他运行多协程的常用方法
1. 使用asyncio.gather()批量等待任务
这是最常用的方式,适合批量创建任务并等待所有任务完成:
async def main(): tasks = [asyncio.create_task(secondCoro(i)) for i in range(10)] await asyncio.gather(*tasks)
2. 使用asyncio.wait()控制任务等待方式
asyncio.wait()可以选择等待所有任务完成(return_when=asyncio.ALL_COMPLETED)或者第一个任务完成(asyncio.FIRST_COMPLETED):
async def main(): tasks = [asyncio.create_task(secondCoro(i)) for i in range(10)] done, pending = await asyncio.wait(tasks, return_when=asyncio.ALL_COMPLETED)
3. Python 3.11+:使用asyncio.TaskGroup()(推荐)
任务组是Python 3.11引入的新特性,提供了更简洁、安全的方式管理异步任务,自动处理异常:
async def main(): async with asyncio.TaskGroup() as tg: for i in range(10): tg.create_task(secondCoro(i)) await asyncio.sleep(0.1) # 退出上下文管理器时,会自动等待所有任务完成
任务组会自动处理任务中的异常,避免因为单个任务崩溃导致整个程序终止,是现代asyncio开发的首选方式。
内容的提问来源于stack exchange,提问作者wisnshaftler




