Define writable method in asyncore client makes sending data very slow

血红的双手。 提交于 2019-12-05 23:08:09

The asyncore loop calls writable() when it is ready to do something with the socket. If the method writable() tells that there is something to write then handle_write() is called. The default writable() always returns True, so in that case there is busy loop calling handle_write() and writable().

In the above implementation the method writable() is called immediately when the client loop is started. At that moment there is nothing in the buffer, so writable() tells that there is nothing to write.

The asyncore loop calls select(). Now the loop is in "standby" state. It can be wakened only when some data is received by the socket or by timeout event. After any of those events the loop again checks writable().

The server sends nothing to the client and the client waits for timeout. The default timeout is 30 seconds, so that is why it is needed to wait up to 30 seconds before something is sent. It is possible to reduce the timeout during starting asyncore.loop():

    asyncore.loop(map = self._thread_sockets, timeout = 0.5)

Another idea that may come here is to check if the buffer is empty in send() and if it is empty send it immediately. However, it is a bad idea. The send() is called in the main thread, but the socket is managed by the asyncore loop in another thread.

For the same reason it makes sense to protect usage of output_buffer for concurrent access from different threads. The lock object threading.Lock() can be used here:

def __init__(self, host, port):
    #...
    self.lock = threading.Lock()

def send(self, msg):
    self.lock.acquire()
    try:
        self.output_buffer.append(msg)
    finally:
        self.lock.release()

def writable(self):
    is_writable = False;
    self.lock.acquire()
    try:
        is_writable = len("".join(self.output_buffer)) > 0
    finally:
        self.lock.release()

    return is_writable

def handle_write(self):
    self.lock.acquire()
    try:
        all_data = "".join(self.output_buffer)
        bytes_sent = self.socket.send(all_data)
        remaining_data = all_data[bytes_sent:]
        self.output_buffer = [remaining_data]
    finally:
        self.lock.release()

There is no thread safe mechanism to waken asyncore from another thread. So, the only solution is to reduce loop timeout, although too small timeout increases CPU usage.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!