asyncio run_until_complete在以后设置结果后发生阻塞(高速公路Web套接字和线程)

时间:2019-08-08 00:29:21

标签: python python-asyncio python-multithreading autobahn

TL; DR::呼叫future.set_result不会立即解决loop.run_until_complete。相反,它会再阻塞5秒钟。

完整上下文: 在我的项目中,我使用autobahn和asyncio通过websocket服务器发送和接收消息。对于我的用例,我需要一个用于Websocket通信的第二个线程,因为我有任意阻塞代码将在主线程中运行。主线程还需要能够安排消息,以便通信线程与服务器来回发送。我当前的目标是使用通信线程传递所有消息,从而发送一条来自主线程的消息,并阻塞直到响应返回为止。

这是我的代码的片段:

import asyncio
import threading
from autobahn.asyncio.websocket import WebSocketClientFactory, WebSocketClientProtocol


CLIENT = None


class MyWebSocketClientProtocol(WebSocketClientProtocol):
    # -------------- Boilerplate --------------
    is_connected = False
    msg_queue = []
    msg_listeners = []

    def onOpen(self):
        self.is_connected = True

        for msg in self.msg_queue[::]:
            self.publish(msg)

    def onClose(self, wasClean, code, reason):
        is_connected = False

    def onMessage(self, payload, isBinary):
        for listener in self.msg_listeners:
            listener(payload)

    def publish(self, msg):
        if not self.is_connected:
            self.msg_queue.append(msg)
        else:
            self.sendMessage(msg.encode('utf-8'))
    # /----------------------------------------

    def send_and_wait(self):
        future = asyncio.get_event_loop().create_future()

        def listener(msg):
            print('set result')
            future.set_result(123)

        self.msg_listeners.append(listener)
        self.publish('hello')

        return future


def worker(loop, ready):
    asyncio.set_event_loop(loop)

    factory = WebSocketClientFactory('ws://127.0.0.1:9000')
    factory.protocol = MyWebSocketClientProtocol
    transport, protocol = loop.run_until_complete(loop.create_connection(factory, '127.0.0.1', 9000))

    global CLIENT
    CLIENT = protocol
    ready.set()

    loop.run_forever()


if __name__ == '__main__':
    # Set up communication thread to talk to the server
    threaded_loop = asyncio.new_event_loop()
    thread_is_ready = threading.Event()
    thread = threading.Thread(target=worker, args=(threaded_loop, thread_is_ready))
    thread.start()
    thread_is_ready.wait()

    # Send a message and wait for response
    print('starting')
    loop = asyncio.get_event_loop()
    result = loop.run_until_complete(CLIENT.send_and_wait())
    print('done') # this line gets called 5 seconds after it should

问题::WebSocketClientProtocol收到对其外发消息的响应,并在其未决的将来调用set_result,但是loop.run_until_complete阻塞了另外4.9秒,直到最终解决。< / p>

我知道run_until_complete还在事件循环中处理其他未决事件。主线程是否可能以某种方式将一堆事件排队,一旦我开始循环就必须立即处理这些事件?另外,如果我将run_until_complete移入通信线程或将create_connection移入主线程,则事件循环不会阻止我。

最后,我尝试在不使用高速公路的情况下重现此问题,但是我无法造成额外的延迟。我很好奇这是否可能与高速公路的回叫时间有关(例如onMessage)。

0 个答案:

没有答案