使用asyncio获取多交易所余额遇事件循环及协程问题求助
异步获取多交易所余额的问题解析与修复
嘿,我来帮你拆解下你遇到的异步编程问题,这些都是异步入门时很常见的坑,慢慢理顺就好啦~
核心错误原因分析
1. RuntimeError: This event loop is already running
这个错误是因为你当前的运行环境(比如Jupyter Notebook、IPython,或者某些GUI框架)已经自动启动了一个事件循环,而asyncio规定同一个线程里不能同时运行多个事件循环:
- 当你调用
loop.run_until_complete()时,它会尝试启动一个新的循环,但发现已有循环在运行,就抛出了这个错误; - 同样,
asyncio.run()内部会创建一个新的事件循环,所以也会和已有的循环冲突,导致报错。
2. RuntimeWarning: coroutine 'xxx.fetch_balance' was never awaited
这个警告有两个可能的触发点:
- 你测试时直接写了
print(exchanges[0].fetchBalance()),这里fetchBalance()是协程函数,调用它只会返回一个协程对象,必须用await才能执行它,所以会触发警告; - 当事件循环冲突的错误发生时,你的
get_balance协程可能没被正确调度执行,导致内部的await exchange.fetchBalance()也没真正被处理,间接触发了警告。
为什么loop.create_task(main())能正常运行?
asyncio.get_running_loop()会获取当前已经在运行的事件循环,然后create_task()是把你的协程添加到这个已有的循环里排队执行,不会尝试创建新循环,所以避开了冲突问题。
针对不同场景的解决方案
场景1:在已有事件循环的环境(Jupyter/IPython等)
直接利用已有的循环,用以下方式提交协程:
async def main(): exchanges = [exchange1, exchange2, ...] results = await get_balances(exchanges) print(results) # 获取当前运行的循环,添加任务 loop = asyncio.get_running_loop() task = loop.create_task(main()) # 如果需要等待结果,可以用await(在交互式环境里直接写await task即可) # await task
或者在Jupyter这类支持直接await的交互式环境里,直接执行:
exchanges = [exchange1, exchange2, ...] results = await get_balances(exchanges) print(results)
场景2:普通Python脚本(无预运行的事件循环)
用asyncio.run()作为程序入口,确保整个异步逻辑都在一个主协程里:
import asyncio # 你的get_balance和get_balances函数保持不变 async def get_balance(exchange, symbol='BTC/USD'): freeBalance = await exchange.fetchBalance() symbol1_balance = freeBalance[symbol.split('/')[0]] symbol2_balance = freeBalance[symbol.split('/')[1]] return symbol1_balance, symbol2_balance async def get_balances(exchanges): futures = [get_balance(exchange) for exchange in exchanges] balances = await asyncio.gather(*futures) return balances async def main(): # 初始化你的交易所实例,比如用ccxt的话: # import ccxt # exchanges = [ccxt.binance({'enableRateLimit': True}), ccxt.bitfinex({'enableRateLimit': True})] exchanges = [exchange1, exchange2, ...] results = await get_balances(exchanges) print(results) if __name__ == "__main__": asyncio.run(main())
额外注意点
- 你提到
print(exchanges[0].fetchBalance())输出协程对象,这是正常的,协程函数必须通过await或者添加到事件循环才能执行; - 注意ccxt库的
fetchBalance()返回结构:大部分交易所返回的是嵌套字典,比如{'BTC': {'free': 0.01, 'used': 0.0}, 'USD': {'free': 100, 'used': 0}},所以你可能需要调整get_balance里的取值逻辑,比如取freeBalance[symbol.split('/')[0]]['free']来获取可用余额; - 异步和多线程的核心区别:多线程是操作系统切换线程,异步是单线程内通过
await主动交出控制权,让其他协程运行,所以所有异步函数都需要被await或者通过任务调度才能执行。
内容的提问来源于stack exchange,提问作者Nicolas Rey
相关产品推荐
相关产品推荐

