Python中asyncio run_in_executor任务done()返回False的原因与解决
问题分析:asyncio中run_in_executor任务done()返回False的原因与解决方法
我在Python 3.10.12环境中运行如下代码:同步函数fetch_data会立即返回1,main函数中包含一个耗时4~5秒的CPU密集型任务sum(i for i in range(int(1e6)))。
原始代码:
import asyncio def fetch_data(): print("done") return 1 async def main(): loop = asyncio.get_event_loop() task = loop.run_in_executor(None, fetch_data) sum(i for i in range(int(1e6))) print(task.done()) asyncio.run(main())
预期输出:
done True
实际输出:
done False
可以看到fetch_data已经执行完成,但task.done()返回False;在print(task.done())前插入await asyncio.sleep(0)后,输出符合预期:
import asyncio def fetch_data(): print("done") return 1 async def main(): loop = asyncio.get_event_loop() task = loop.run_in_executor(None, fetch_data) sum(i for i in range(int(1e6))) await asyncio.sleep(0) # 插入后输出符合预期 print(task.done()) asyncio.run(main())
原因解析
- asyncio事件循环的工作逻辑:asyncio的事件循环是单线程模型,依赖任务切换来实现异步。调用
loop.run_in_executor时,fetch_data会被放到线程池执行,但事件循环不会立刻处理该任务完成后的回调逻辑。 - CPU密集型任务阻塞事件循环:
sum(i for i in range(int(1e6)))会长时间占用事件循环所在的线程,导致事件循环无法及时处理线程池中fetch_data完成后的回调——也就是更新task的状态为已完成。 await asyncio.sleep(0)的作用:这个操作会触发事件循环切换任务,让它有机会处理挂起的回调队列,包括更新task的完成状态,因此之后调用task.done()会返回True。
不使用asyncio.sleep(0)的解决方法
有两种可靠的实现方式:
- 直接
await任务:run_in_executor返回的是Future对象,直接await它会让事件循环等待任务完成,自然能正确获取任务状态:
import asyncio def fetch_data(): print("done") return 1 async def main(): loop = asyncio.get_event_loop() task = loop.run_in_executor(None, fetch_data) sum(i for i in range(int(1e6))) await task # 等待任务完成 print(task.done()) # 此时必然返回True asyncio.run(main())
- 手动触发事件循环处理回调:可以调用
loop.process_events()(低层级API,需谨慎使用),但更推荐使用await的方式,代码可读性和稳定性更高。
内容的提问来源于stack exchange,提问作者Lva Ciee
相关产品推荐
相关产品推荐

