首页 > 解决方案 > 在此异步设置中,我在哪里捕获 KeyboardInterrupt 异常

问题描述

我正在开发一个使用ccxt异步库的项目,该库需要通过显式调用该类的协程来释放某个类使用的所有资源.close()。我想退出程序ctrl+c并等待异常中的关闭协程。然而,它永远不会被期待。

该应用程序由模块harvesters、、、、strategiestraders(加上配置等)broker组成。main经纪人启动为交易所指定的策略并执行它们。该策略启动相关的收集器,收集必要的数据。它还分析数据并在有获利机会时催生交易者。主模块为每个交易所创建一个代理并运行它。我试图在每个级别捕获异常,但从不等待关闭例程。我宁愿在主模块中捕获它以关闭所有交换实例。

收割机

async def harvest(self):
    if not self.routes:
        self.routes = await self.get_routes()
    for route in self.routes:
        self.logger.info("Harvesting route {}".format(route))
        await asyncio.sleep(self.exchange.rateLimit / 1000)
        yield await self.harvest_route(route)

战略

async def execute(self):
    async for route_dct in self.harvester.harvest():
        self.logger.debug("Route dictionary: {}".format(route_dct))
        await self.try_route(route_dct)

经纪人

async def run(self):
    for strategy in self.strategies:
        self.strategies[strategy] = getattr(
            strategies, strategy)(self.share, self.exchange, self.currency)
    while True:
        try:
            await self.execute_strategies()
        except KeyboardInterrupt:
            await safe_exit(self.exchange)

主要的

async def main():
    await load_exchanges()
    await load_markets()
    brokers = [Broker(
        share,
        exchanges[id]["api"],
        currency,
        exchanges[id]["strategies"]
        ) for id in exchanges]
    futures = [broker.run() for broker in brokers]
    for future in asyncio.as_completed(futures):
        executed = await future
        return executed


if __name__ == "__main__":
    status = asyncio.run(main())
    sys.exit(status)

我曾预计close()协程会被等待,但我仍然从库中收到一个错误,我必须明确调用它。我在哪里捕获异常以便正确关闭所有交换实例?

标签: pythonasynchronouspython-asyncioccxt

解决方案


Somewhere in your code should be entry point, where event loop is started.

Usually it is one of functions below:

loop.run_until_complete(main())

loop.run_forever()

asyncio.run(main())

When ctrl+C happens KeyboardInterrupt can be catched at this line. When it happened to execute some finalizing coroutine you can run event loop again.

This little example shows idea:

import asyncio 


async def main():
    print('Started, press ctrl+C')
    await asyncio.sleep(10)


async def close():
    print('Finalazing...')
    await asyncio.sleep(1)


if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    try:
        loop.run_until_complete(main())
    except KeyboardInterrupt:
        loop.run_until_complete(close())
    finally:
        print('Program finished')

推荐阅读