Python 使用线程与套接字通信

Python 使用线程与套接字通信,python,multithreading,sockets,Python,Multithreading,Sockets,我目前正在编写一个python类,它充当客户机。 因为我不想阻塞主线程,所以数据包的接收在另一个线程中完成,如果数据包到达,则调用回调函数 接收到的数据包要么是广播消息,要么是对客户端发送的命令的回复。发送命令的功能是同步的,它会一直阻塞直到回复到达,因此它可以直接返回结果 简化示例: import socket import threading class SocketThread(threading.Thread): packet_received_callback = None

我目前正在编写一个python类,它充当客户机。 因为我不想阻塞主线程,所以数据包的接收在另一个线程中完成,如果数据包到达,则调用回调函数

接收到的数据包要么是广播消息,要么是对客户端发送的命令的回复。发送命令的功能是同步的,它会一直阻塞直到回复到达,因此它可以直接返回结果

简化示例:

import socket
import threading

class SocketThread(threading.Thread):
    packet_received_callback = None

    _reply = None
    _reply_event = threading.Event()

    def run(self):
        self._initialize_socket()

        while True:
            # This function blocks until a packet arrives
            p = self._receive_packet()

            if self._is_reply(p):
                self._reply = p
                self._reply_event.set()
            else:
                self.packet_received_callback(p)

    def send_command(self, command):
        # Send command via socket
        self.sock.send(command)

        # Wait for reply
        self._reply_event.wait()
        self._reply_event.clear()

        return self._process_reply(self._reply)
我现在面临的问题是,我无法在回调函数中发送命令,因为这将导致死锁(send_命令等待应答,但无法接收数据包,因为接收数据包的线程实际上正在执行回调函数)

我目前的解决方案是每次启动一个新线程来调用回调函数。但是,这样会产生大量线程,并且很难确保在高流量情况下同步处理数据包

有人知道一个更优雅的解决方案吗?或者我走对了吗


谢谢你的帮助

这个问题的正确答案在很大程度上取决于您试图解决的问题的细节,但这里有一个解决方案:


我认为套接字线程只存储它接收到的数据包并继续轮询数据包比在接收到数据包时立即调用回调函数更有意义。然后,当主线程有时间时,它可以检查已经到达的新数据包并对其进行操作

最近有了另一个想法,请告诉我您的想法。这只是一种解决此类问题的通用方法,以防其他人遇到类似问题并需要使用多线程

import threading
import queue

class EventBase(threading.Thread):
    ''' Class which provides a base for event-based programming. '''

    def __init__(self):
        self._event_queue = queue.Queue()

    def run(self):
        ''' Starts the event loop. '''

        while True:
            # Get next event
            e = self._event_queue.get()

            # If there is a "None" in the queue, someone wants to stop
            if not e:
                break

            # Call event handler
            e[0](*e[1], **e[2])
            # Mark as done
            self._event_queue.task_done()

    def stop(self, join=True):
        ''' Stops processing events. '''

        if self.is_alive():
            # Put poison-pill to queue
            self._event_queue.put(None)
            # Wait until finished
            if join:
                self.join()

    def create_event_launcher(self, func):
        ''' Creates a function which can be used to call the passed func in the event-loop. '''

        def event_launcher(*args, **kwargs):
            self._event_queue.put((func, args, kwargs))

        return event_launcher
像这样使用它:

event_loop = eventbase.EventBase()
event_loop.start()

# Or any other callback
sock_thread.packet_received_callback = event_loop.create_event_launcher(my_event_handler)

# ...

# Finally
event_loop.stop()

通常情况下,这是最好使用twisted库来完成的事情。试图通过线程对象自己完成这项工作可能会导致很多混乱。例如,您是否熟悉围绕全局解释器锁(GIL)的线程问题?谢谢您的快速回复。不,我从没听说过。我将做一些研究并查看该库。我认为线程是一种有效的方法,因为Event.wait()和socket.recv()都应该在等待时释放GIL。当然,这取决于所做工作的细节。当然,这可以通过线程来完成,只要你小心地使用那些释放GIL的函数就可以了,但总的来说,我认为运行自己的多线程网络应用程序是相当困难的,而且充满了陷阱,twisted或其他成熟的网络库非常有帮助。考虑到您提到的问题(找到一篇有趣的文章),我想我将试着设计单线程的应用程序。协议很简单(正如我在下面的评论中提到的那样),我认为加载这么大的库是不值得的。我认为这样额外的线程就不再有用了。使用socket.setblocking(False)函数并使整个应用程序成为单线程不是更容易吗?该应用程序用于处理接收到的IR命令,并使用(使用unix套接字接口)发送IR命令。因为我几个月前才开始学习python,所以我认为学习python中的多线程是一个不错的项目。