如何await返回字典的请求?关于asyncio处理阻塞函数的疑问
asyncio阻塞函数调用问题与多线程适配解答
问题背景
尝试用asyncio处理耗时任务,现有代码中get_batch_result是一个返回字典的阻塞函数,使用await修饰时触发错误:object dict can't be used in 'await' expression,同时有两个疑问:
- 是否只有非阻塞的可等待函数才能进行异步调用?
- 多线程方案是否适用于该场景?
原代码如下:
async def get_features(nbatch): """Do something to my batch""" print(f"doing {nbatch}") features = await get_batch_result(nbatch) print(f"finishing {nbatch}") return features async def get_them_all(): nbatches = [1,2,3,4,5] return await asyncio.gather(*(get_features(nbatch) for nbatch in nbatches)) elements = asyncio.run(get_them_all())
解答
1. 只有可等待对象才能被await
是的,await关键字只能作用于可等待对象(awaitable objects),这类对象包括:
- 协程(coroutine)
- 任务(Task)
- 未来对象(Future)
普通阻塞函数直接返回字典(普通Python对象),不属于可等待对象范畴,因此无法用await修饰。asyncio的异步调度依赖非阻塞的可等待对象来实现任务切换,阻塞函数会直接卡住整个事件循环,完全失去异步并发的意义。
2. 多线程方案完全适配该场景
你的核心瓶颈是耗时的阻塞函数,不管是IO密集型(比如网络请求、文件读写)还是CPU密集型任务,都可以通过asyncio结合线程池来处理,既不阻塞事件循环,又能并行执行这些耗时任务。
修改后的代码示例(Python 3.9+)
使用asyncio.to_thread将阻塞函数委托给线程池执行:
import asyncio # 模拟阻塞的耗时函数 def get_batch_result(nbatch): import time time.sleep(2) # 模拟耗时操作 return {"batch_id": nbatch, "features": [1, 2, 3]} async def get_features(nbatch): print(f"starting batch {nbatch}") # 将阻塞函数放到线程池执行,返回可等待对象供await调用 features = await asyncio.to_thread(get_batch_result, nbatch) print(f"completed batch {nbatch}") return features async def get_them_all(): nbatches = [1,2,3,4,5] # 并发执行所有任务 return await asyncio.gather(*(get_features(nbatch) for nbatch in nbatches)) elements = asyncio.run(get_them_all()) print("All results:", elements)
兼容Python 3.8及以下版本的写法
手动获取事件循环,使用loop.run_in_executor:
async def get_features(nbatch): print(f"starting batch {nbatch}") loop = asyncio.get_running_loop() # None表示使用默认线程池,也可自定义ThreadPoolExecutor指定线程数 features = await loop.run_in_executor(None, get_batch_result, nbatch) print(f"completed batch {nbatch}") return features
内容的提问来源于stack exchange,提问作者dguerrero
相关产品推荐
相关产品推荐

