在单独的线程中同步运行异步代码

时间:2018-02-08 22:15:16

标签: django python-asyncio django-channels

我正在使用Django频道来支持websockets,并使用他们的组概念向同一组中的多个消费者广播消息。为了在消费者之外发送消息,您需要在其他同步代码中调用异步方法。不幸的是,这在测试时会出现问题。

我开始使用loop.run_until_complete

loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.ensure_future(channel_layer.group_send(group_name, {'text': json.dumps(message),
                                                                                    'type': 'receive_group_json'}),
                                              loop=loop))

然后堆栈跟踪读取该线程没有事件循环:RuntimeError: There is no current event loop in thread 'Thread-1'.。为了解决这个问题,我补充道:

loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
loop.run_until_complete(asyncio.ensure_future(channel_layer.group_send(group_name, {'text': json.dumps(message),
                                                                                    'type': 'receive_group_json'}),
                                              loop=loop))

现在堆栈跟踪正在读取RuntimeError: Event loop is closed,但如果我添加打印语句loop.is_closed()则会打印False

对于上下文,我使用的是Django 2.0,Channels 2和redis后端。

更新:我尝试在Python解释器中运行它(在py.test之外删除移动变量)。当我运行第二个代码块时,我没有收到Event loop is closed错误(这可能是由于Pytest的结果是否有超时等)。但是,我没有在我的客户端收到群组消息。但是,我确实看到了一份印刷声明:

({<Task finished coro=<RedisChannelLayer.group_send() done, defined at /Users/my/path/to/venv/lib/python3.6/site-packages/channels_redis/core.py:306> result=None>}, set())

更新2 :刷新redis之后,我在py.test中添加了一个fixture来为每个函数和会话范围的事件循环刷新它。这次又来自RedisChannelLayer的另一张照片:

({<Task finished coro=<RedisChannelLayer.group_send() done, defined at /Users/my/path/to/venv/lib/python3.6/site-packages/channels_redis/core.py:306> exception=RuntimeError('Task <Task pending coro=<RedisChannelLayer.group_send() running at /Users/my/path/to/venv/lib/python3.6/site-packages/channels_redis/core.py:316>> got Future <Future pending> attached to a different loop',)>}, set())

2 个答案:

答案 0 :(得分:1)

如果SELECT * , AVG(MonthlyStat) OVER (ORDER BY CAST(REPLACE(Calendar_Date,' ','') AS DATE) ASC ROWS BETWEEN 11 PRECEDING AND CURRENT ROW) AS YearlyStat FROM YourTable 期望在另一个线程中驻留在自己的事件循环中,则需要保留该事件循环对象。一旦你拥有了它,你就可以向它提交协同程序并与你的线程同步,如下所示:

channel_layer

答案 1 :(得分:1)

默认情况下,只有主线程获取事件循环,并且在其他线程中调用get_event_loop将失败。

如果您需要另一个线程中的事件循环 - 例如处理HTTP或WebSockets请求的线程 - 您需要使用new_event_loop自行创建。之后,您可以使用set_event_loop,将来的get_event_loop来电也可以使用。我这样做:

# get or create an event loop for the current thread
def get_thread_event_loop():
    try:
        loop = asyncio.get_event_loop()  # gets previously set event loop, if possible
    except RuntimeError:
        loop = asyncio.new_event_loop()
        asyncio.set_event_loop(loop)
    return loop

More here