创建线程化zeromq套接字的正确方法是什么?

时间:2015-04-15 15:53:16

标签: python thread-safety pyzmq

我想知道如何正确创建后台线程,该后台线程会收听一些随机端口并将接收到的对象推送到队列?

我希望我的套接字包装器启动新线程,选择一些随机端口并开始监听。我必须能够从套接字包装器中获取此端口号。

我想出了简单的课程:

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套接字不能在线程之间共享,它们不是线程安全的(http://api.zeromq.org/2-1:zmq)。因此,套接字创建和绑定应移至run()方法:

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()时,它会引入死锁的风险。使用Thread的_started Event:

可以避免这种情况
    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

这是最后的线程安全吗?还有什么需要照顾的吗?

我仍然在这里看到的问题是我想在创建SocketWrapper后立即获得端口。我可以安全地在start()中调用线程__init__吗?

1 个答案:

答案 0 :(得分:0)

我最后修改了这个解决方案以避免死锁主线程:

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

这并不完美。因为我们延迟了get_port,但它很简单并完成了工作。有任何建议如何改进吗?