无法让我的异步功能正常工作

时间:2019-08-31 03:44:18

标签: python python-3.x websocket jupyter-notebook python-asyncio

我正在尝试基于WebSocket提供的数据构建脚本,但是我有一个棘手的问题我无法解决。我有两个牢房。

第一个:

msg = ''

stream = {}

async def call_api(msg):
    async with websockets.connect('wss://www.bitmex.com/realtime?subscribe=quote:XBTUSD,quote:ETHU19') as websocket:
        await websocket.send(msg)
        while websocket.open:
            response = await websocket.recv()
            response = json.loads(response)

            if 'data' in list(response.keys()):

                response = response['data']

                for n in range(len(response)):

                    symbol = response[n]['symbol']

                    stream[symbol] = response[n]['askPrice']


loop = asyncio.get_event_loop()
loop.create_task(call_api(msg))

第二个:

stream['XBTUSD']

如果我在Jupyter Notebook中运行第一个单元格,然后再手动运行第二个单元格,则python将输出正确的值。但是,如果按下“重新启动当前内核并重新执行整个笔记本”按钮,第二个单元格将显示错误KeyError: 'XBTUSD'。当我使用python shell运行脚本时,也会发生此错误。

我无法理解这两个执行之间的行为差​​异。

1 个答案:

答案 0 :(得分:2)

这是因为您在第一个单元格中创建了asyncio任务,但没有 not 等待它完成。 loop.create_task()立即返回并让事件循环在活动循环还存在的情况下继续在后台执行创建的任务。 (在这种情况下,事件循环将在笔记本内核运行时继续运行。)因此,loop.create_task()使Jupyter笔记本认为第一个单元格立即完成。

请注意,Jupyter笔记本本身也针对内核进程异步工作,因此,如果您在第一个单元之后运行第二个单元太快(例如,使用“重新启动当前内核并重新执行整个笔记本”),而不是手动单击(运行按钮),则第一个单元格的异步任务将在第二个单元格的执行开始之前完成。

要确保第一个单元格在报告单元格执行完成之前实际完成了任务,请使用run_until_complete()而不是create_task()

loop = asyncio.get_event_loop()
loop.run_until_complete(call_api(msg))

或者,通过对任务的引用来获得对任务的额外控制:

loop = asyncio.get_event_loop()
t = loop.create_task(call_api(msg))
loop.run_until_complete(t)

如果您想让任务无限期地在后台运行,则需要使用其他方法。

  • 不要使用Jupyter笔记本,而要编写守护进程来连续获取和处理Websocket消息。 Jupyter没有提供任何方法来跟踪内核进程中的后台异步任务并通过此类后台任务的事件触发器执行单元。 Jupyter笔记本根本不是用于这种模式的工具。
  • 要使websocket消息接收器与处理例程脱钩,请使用中间队列。如果双方在相同的进程中运行并且在相同的事件循环中运行,则可以使用asyncio.Queue。如果使用同步代码在不同的线程中进行处理,则可以尝试janus。如果处理是在另一个过程中进行的,请使用multiprocessing.Queue或其他一些IPC机制。