即使在上下文终止后,ZMQ recv()也会阻塞

时间:2013-09-15 10:10:10

标签: c++ multithreading zeromq

我尽力按照ZMQ termination whitepaper中的说明进行操作,但到目前为止,我失败了。 我有一个父类,它产生一个监听器线程(使用win32-pthreads)。

根据白皮书的说法,在终止时,我应该设置_stopped标记,删除上下文,然后调用zmq_term()并释放阻止recv()。相反,我得到的是:

  • 调用delete _zmqContext会导致应用程序崩溃(可能存在分段错误)
  • 使用zmq_term(_zmqContext)替换删除操作不会释放阻止recv()

我正在添加部分代码示例,这很长,因为我不确定哪个部分可能很重要。

AsyncZmqListener.hpp:

class AsyncZmqListener
{
public:
    AsyncZmqListener(const std::string uri);
    ~AsyncZmqListener();

    bool Start();
    void Stop();

private:
    static void* _threadEntryFunc(void* _this);     
    void _messageLoop();

private:
    bool _stopped;
    pthread_t _thread;
    zmq::context_t* _zmqContext;
};

AsyncZmqListener.cpp:

AsyncZmqListener::AsyncZmqListener(const std::string uri) : _uri(uri)
{
    _zmqContext = new zmq::context_t(1);
    _stopped = false;
}

void AsyncZmqListener::Start()
{
    int status = pthread_create(&_thread, NULL, _threadEntryFunc, this);
}

void AsyncZmqListener::Stop()
{
    _stopped = true;
    delete _zmqContext;             // <-- Crashes the application. Changing to 'zmq_term(_zmqContext)' does not terminate recv()
    pthread_join(_thread, NULL);    // <-- This waits forever
}

void AsyncZmqListener::_messageLoop()
{        
    zmq::socket_t listener(*_zmqContext, ZMQ_PULL);
    listener.bind(_uri.c_str());

    zmq::message_t message;    
    while(!_stopped)
    {
        listener.recv(&message);    // <-- blocks forever
        process(message);
    }
}

P.S。

我知道这个related question,但没有一个答案与白皮书中描述的干净退出流程完全匹配。如果必须,我会决定进行民意调查......

1 个答案:

答案 0 :(得分:4)

ZMQ recv()在相关上下文终止后取消阻止

当发生这种情况时,我不知道recv()会抛出ETERM异常。 修改后的代码:

void AsyncZmqListener::_messageLoop()
{        
    zmq::socket_t listener(*_zmqContext, ZMQ_PULL);
    listener.bind(_uri.c_str());

    zmq::message_t message;    
    while(!_stopped)
    {
        try
        {
            listener.recv(&message);
            process(message);
        }
        catch(const zmq::error_t& ex)
        {
            // recv() throws ETERM when the zmq context is destroyed,
            //  as when AsyncZmqListener::Stop() is called
            if(ex.num() != ETERM)
                throw;
        }
    }
}