asyncio能否封装同步REST调用?三个请求未并发总耗时叠加是什么原因
问题原因分析
- 你封装的异步函数里直接调用了同步阻塞的
fetch_positions()方法。同步IO调用会直接阻塞整个asyncio事件循环,哪怕你套了async关键字,这段代码执行时事件循环无法切换到其他任务,等同于同步运行。 - 你连续三次调用
loop.run_until_complete(),这个方法本身是阻塞的,必须等传入的协程执行完成才会返回,三次调用天然就是串行执行,不可能并行。 - 额外说明:
nest_asyncio在这里属于不必要的引入,没有解决核心问题还可能带来事件循环嵌套的隐患。
正确实现方案
CCXT库本身已经提供了所有REST接口的异步实现,优先推荐使用官方异步版本,不需要自己做同步转异步封装,改完后三次接口调用会并行发起,总耗时接近单次调用的300ms:
import asyncio import ccxt.async_support as ccxt # 导入ccxt官方异步支持模块 # 初始化三个异步exchange实例 exchange1 = ccxt.ftx({'apiKey': '你的API_KEY', 'secret': '你的SECRET'}) exchange2 = ccxt.ftx({'apiKey': '你的API_KEY', 'secret': '你的SECRET'}) exchange3 = ccxt.ftx({'apiKey': '你的API_KEY', 'secret': '你的SECRET'}) async def main(): # 把三个协程任务打包,用gather并行执行 pos1, pos2, pos3 = await asyncio.gather( exchange1.fetch_positions(), exchange2.fetch_positions(), exchange3.fetch_positions() ) # 后续处理pos1/pos2/pos3的逻辑 print(pos1, pos2, pos3) # 用完记得关闭exchange实例释放连接 await exchange1.close() await exchange2.close() await exchange3.close() if __name__ == "__main__": asyncio.run(main())
特殊情况:必须调用原始同步方法的解决方案
如果因为特殊原因必须调用同步版本的fetch_positions(),就需要把同步方法放到线程池里执行,避免阻塞事件循环,示例如下:
import asyncio import ccxt from concurrent.futures import ThreadPoolExecutor # 初始化同步exchange实例 exchange1 = ccxt.ftx({'apiKey': '你的API_KEY', 'secret': '你的SECRET'}) exchange2 = ccxt.ftx({'apiKey': '你的API_KEY', 'secret': '你的SECRET'}) exchange3 = ccxt.ftx({'apiKey': '你的API_KEY', 'secret': '你的SECRET'}) executor = ThreadPoolExecutor(max_workers=3) loop = asyncio.get_event_loop() async def main(): # 把同步调用提交到线程池,转为可等待对象 task1 = loop.run_in_executor(executor, exchange1.fetch_positions) task2 = loop.run_in_executor(executor, exchange2.fetch_positions) task3 = loop.run_in_executor(executor, exchange3.fetch_positions) pos1, pos2, pos3 = await asyncio.gather(task1, task2, task3) print(pos1, pos2, pos3) if __name__ == "__main__": loop.run_until_complete(main())
内容的提问来源于stack exchange,提问作者user3761555
相关产品推荐
相关产品推荐

