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记忆不如我想象的那么好!既然没有更好的答案,我就接受我的答案。