Python 创建线程化zeromq套接字的正确方法是什么?
我想知道如何正确地创建后台线程,它将侦听一些随机端口并将接收到的对象推送到队列中 我希望我的套接字包装器启动新线程,选择一些随机端口并开始在中侦听。我必须能够从套接字包装器获取此端口号 我提出了一个简单的类:Python 创建线程化zeromq套接字的正确方法是什么?,python,thread-safety,pyzmq,Python,Thread Safety,Pyzmq,我想知道如何正确地创建后台线程,它将侦听一些随机端口并将接收到的对象推送到队列中 我希望我的套接字包装器启动新线程,选择一些随机端口并开始在中侦听。我必须能够从套接字包装器获取此端口号 我提出了一个简单的类: class SocketWrapper(Thread): def __init__(self, socket_type, *args, **kwargs): super(Thread, self).__init__(*args, **kwargs)
class SocketWrapper(Thread):
def __init__(self, socket_type, *args, **kwargs):
super(Thread, self).__init__(*args, **kwargs)
self._ctx = zmq.Context()
self._socket = self._ctx._socket(socket_type)
self.port = self._socket.bind_to_random_port('tcp://*')
self._queue = Queue()
def run(self):
while not self.stop_requested:
try:
item = socket.recv_pyobj(flags=zmq.NOBLOCK)
self._queue.put(item)
except ZMQError:
time.sleep(0.01) # Wait a little for next item to arrive
但是,zmq套接字不能在线程之间共享,它们不是线程安全的()。因此,套接字创建和绑定应移动到run()
method:
class SocketWrapper2(Thread):
def __init__(self, socket_type, *args, **kwargs):
super(Thread, self).__init__(*args, **kwargs)
self._socket_type = socket_type
self._ctx = zmq.Context()
self._queue = Queue()
self._event = Event()
def run(self):
socket = self._ctx._socket(self._socket_type)
self.port = self._socket.bind_to_random_port('tcp://*')
self._event.set()
while not self.stop_requested:
try:
item = socket.recv_pyobj(flags=zmq.NOBLOCK)
self._queue.put(item)
except ZMQError:
time.sleep(0.01) # Wait a little for next item to arrive
def get_port(self):
self._event.wait()
return self.port
在读取之前,我必须添加事件以确保端口已经绑定,但当在start()之前调用SocketWrapper2.get_port()时,它会引入死锁风险。这可以通过使用线程的_start事件来避免:
def get_port(self):
if not self._started.is_set():
raise RuntimeError("You can't call run_port before thread start.")
self._event.wait()
return self.port
这是线程安全的吗?还有什么要处理的吗
我在这里仍然看到的问题是,我希望在创建SocketTrapper之后立即获得端口。我是否可以在
\uuu init\uuuu
中安全地调用线程的start()
?我最后稍微修改了这个解决方案,以避免主线程死锁:
def get_port(self):
if not self._started.is_set():
raise RuntimeError("You can't call run_port before thread start.")
if not self._event.wait(1):
raise RuntimeError("Couldn't get port after a while.")
return self.port
这并不完美。因为我们延迟了获取端口,但它很简单,并且可以完成任务。有什么建议可以改进吗?
zmq.Context()
应该在线程中调用。将其移动到run
方法。而且您没有代码来处理线程中的错误。如果在self.\u event.set()
之前发生了某些事情,您仍然会死锁。如果以后发生什么事。。。你想发生什么?@tdelaney根据zmq。上下文是线程安全的。但你是对的。self.\u event.set()
之前的任何异常都将永远死锁MainThread.Oops。。。我的zmq记忆不如我想象的那么好!既然没有更好的答案,我就接受我的答案。