如何用loop.run_until_complete()交替运行Python多个异步函数?
我尝试在Python中使用loop.run_until_complete()交替运行两个异步函数test1()和test2(),代码如下:
import asyncio async def test1(): for _ in range(3): print("Test1") await asyncio.sleep(1) async def test2(): for _ in range(3): print("Test2") await asyncio.sleep(1) loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) loop.run_until_complete(test1()) # 此处 loop.run_until_complete(test2()) # 此处
但实际运行结果是先执行完test1再执行test2,无法交替:
Test1 Test1 Test1 Test2 Test2 Test2
我知道使用loop.run_forever()结合loop.create_task()可以实现交替运行,但loop.run_forever()会一直运行,需要额外添加loop.stop()代码来停止,较为繁琐。另外,asyncio.gather()也能实现交替运行,但不想在调用run_until_complete的上下文里使用await关键字。那么,如何用loop.run_until_complete()实现异步函数的交替运行?
解决方案
核心是把两个异步任务打包成一个并发执行的awaitable对象,再传给loop.run_until_complete(),让事件循环在单次调用中调度两个任务,而非串行执行两次。
方法1:用asyncio.gather()直接封装
asyncio.gather()可以把多个awaitable对象打包成一个整体,loop.run_until_complete()会自动等待所有任务完成后退出,无需额外停止逻辑。虽然gather内部依赖await,但不需要你在调用run_until_complete的地方显式写await:
import asyncio async def test1(): for _ in range(3): print("Test1") await asyncio.sleep(1) async def test2(): for _ in range(3): print("Test2") await asyncio.sleep(1) loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) # 将两个任务打包后传给run_until_complete loop.run_until_complete(asyncio.gather(test1(), test2()))
运行后会交替输出Test1和Test2,所有任务完成后事件循环自动终止。
方法2:手动创建任务+包装函数
如果不想用gather,可以手动创建任务,用一个包装函数等待所有任务完成,再把包装函数传给run_until_complete():
import asyncio async def test1(): for _ in range(3): print("Test1") await asyncio.sleep(1) async def test2(): for _ in range(3): print("Test2") await asyncio.sleep(1) loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) async def run_concurrent_tasks(): # 创建两个异步任务 task1 = loop.create_task(test1()) task2 = loop.create_task(test2()) # 等待两个任务全部完成 await task1 await task2 loop.run_until_complete(run_concurrent_tasks())
这种方式同样能实现交替运行,run_until_complete会等待包装函数执行完毕,而包装函数内的两个任务是并发调度的。
原代码无法交替的原因
你之前连续两次调用loop.run_until_complete(),相当于让事件循环先完整执行完test1的整个生命周期(从开始到所有await都执行完毕),再去启动并执行test2,本质是串行执行两个任务,自然无法交替。只有让两个任务在同一个事件循环的单次run_until_complete调用中被调度,才能利用异步IO的调度机制实现交替执行。
内容的提问来源于stack exchange,提问作者Super Kai - Kazuya Ito

