我正在编写一个充当客户端的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_command等待回复,但是因为接收数据包的线程实际上没有接收到数据包执行回调函数)。
我目前的解决方案是每次启动一个新线程来调用回调函数。但是这样会产生很多线程,并且很难确保在繁忙的交通情况下同步处理数据包。
有人知道更优雅的解决方案还是我走的正确?
感谢您的帮助!
答案 0 :(得分:0)
这个问题的正确答案很大程度上取决于你要解决的问题的细节,但这是一个解决方案:
我认为套接字线程只是简单地存储它收到的数据包并继续轮询数据包,而不是在收到数据包后立即调用回调函数。然后,当主线程有时间时,它可以检查已到达的新数据包并对其进行操作。
答案 1 :(得分:0)
最近有了另一个想法,让我知道你是怎么看待它的。这只是解决此类问题的一般方法,以防其他人遇到类似问题并需要使用多线程。
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()