C++ ZeroMQ在context.close()中被阻止。如何在C++;? 描述

C++ ZeroMQ在context.close()中被阻止。如何在C++;? 描述,c++,sockets,zeromq,C++,Sockets,Zeromq,广播员在酒吧插座中广播消息”tcp://localhost:5556“和另一个酒吧插座中的停止控制信号”tcp://localhost:5557". 侦听器接收消息。它一听到停止控制信号就停止并退出 正如0MQ终止白皮书中所述,要停止recv()等待,终止底层上下文是一种标准方法,recv()将退出并抛出一个ETERM EXCEPTION 虽然recv()块被释放,但context.close()被阻止。因此,程序仍然无法安全退出 此外,在上下文之前关闭套接字,并将订户套接字的延迟值设置为0。尽

广播员在酒吧插座中广播消息”tcp://localhost:5556“和另一个酒吧插座中的停止控制信号”tcp://localhost:5557". 侦听器接收消息。它一听到停止控制信号就停止并退出

正如0MQ终止白皮书中所述,要停止recv()等待,终止底层上下文是一种标准方法,recv()将退出并抛出一个ETERM EXCEPTION

虽然recv()块被释放,但context.close()被阻止。因此,程序仍然无法安全退出

此外,在上下文之前关闭套接字,并将订户套接字的延迟值设置为0。尽管如此,它还是被封锁了

  • [系统]:Ubuntu 18.04.1(Linux 4.18.0-17-generic)
  • [编译器]:gcc-g++7.3.0版
  • [ZeroMQ]:libzmq 4.3.1+cppzmq 4.3.0
复制代码 下面是一个简单但完整的代码来重现这个问题

//类广播:广播对象每10毫秒发送一条消息,
//最后发送停止控制信号
//-start():开始广播。
班级广播{
公众:
广播():
上下文(1),
出版商(上下文、ZMQ、PUB),
控制器(上下文、ZMQ、PUB)
{
发布者绑定(“tcp://*:5556”);
控制器绑定(“tcp://*:5557”);
}
void start(){

std::cout我总是将zmq上下文置于主线程的控制之下

伪代码:

main()
{
context(1) // only one context
job1(context) // pass ref to the one context
job2(context) // pass ref to the one context
job1.join()
job2.join()
context.close()
}
如果你的结构意味着你不能这样做,那么你需要更多地考虑如何处理关闭

您的代码正在一个线程中调用控制代码(在套接字上)

subscriber_.close();
controller_.close();
context_.close();
以及另一个中的处理代码(在套接字上)

controller_.recv(&ctrl);
不这样做的两个原因

  • zmq标准插座不是螺纹安全的
  • 您有一个竞争条件,当recv线程阻塞时,套接字和上下文可能会被破坏。当它取消阻塞时,它将被破坏 只是会以一种未定义的方式失败,因为zmq实际上已经死了(上下文关闭) 这一点

您应该在同一线程中打开、使用和关闭套接字。在这种情况下(订户对象)在
start()中的套接字上调用
close()
线程当您得到
etrem
停止
为真时

感谢@James的回答,我自己解决了这个问题,并通过以下更改更新了代码:

  • main()
    中管理上下文,并将其与
    std::shared_ptr
    一起传递
  • 关闭线程中的套接字创建它
  • 广播额外消息以刷新zmq队列
  • 使用
    std::atomic
    而不是
    volatile
    键入标志
    stop
  • 因此,我们不必残忍地破坏
    上下文
    并捕获
    etrem
    ,这是一种不自然的方式。所有线程上的所有套接字都可以安全退出

    最后,我在这里发布了源代码。希望它能帮助其他遇到同样问题的人

    类广播{
    公众:
    广播(标准::共享上下文):
    上下文(context),,
    出版商(*context,ZMQ_PUB),
    控制器(*上下文,ZMQ\u发布)
    {
    发布者绑定(“tcp://*:5556”);
    控制器绑定(“tcp://*:5557”);
    }
    ~Broadcast(){
    publisher_.close();
    控制器关闭();
    }
    void start(){
    
    std::这个问题写得很好!如果将
    std::launch::async
    传递给
    std::async
    ,这个问题还会发生吗?谢谢你的评论。对于
    std::lanuch::async
    std::lanuch::deferred
    这两个问题都会发生,并且使用
    std::thread
    join()
    也一样。@Filipp谢谢你,James。是的,在不同的线程中操作套接字是不安全的。在main下传递上下文的框架是一个很好的做法。我通过使用你的建议来解决这个问题,并在调用stop后发送一条额外的消息。
    #0  __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:51
    #1  0x00007f90dd99a801 in __GI_abort () at abort.c:79
    #2  0x00007f90de57a52e in zmq::zmq_abort(char const*) () from /usr/local/lib/libzmq.so.5
    #3  0x00007f90de59ca67 in zmq::signaler_t::wait(int) () from /usr/local/lib/libzmq.so.5
    #4  0x00007f90de57ea5c in zmq::mailbox_t::recv(zmq::command_t*, int) () from /usr/local/lib/libzmq.so.5
    #5  0x00007f90de59e9c7 in zmq::socket_base_t::process_commands(int, bool) () from /usr/local/lib/libzmq.so.5
    #6  0x00007f90de59f726 in zmq::socket_base_t::recv(zmq::msg_t*, int) () from /usr/local/lib/libzmq.so.5
    #7  0x00007f90de5c4e8c in zmq_msg_recv () from /usr/local/lib/libzmq.so.5
    #8  0x0000561da8eb18f3 in zmq::socket_t::recv(zmq::message_t*, int) ()
    #9  0x0000561da8eb2b47 in Listener::start() ()
    
    main()
    {
    context(1) // only one context
    job1(context) // pass ref to the one context
    job2(context) // pass ref to the one context
    job1.join()
    job2.join()
    context.close()
    }
    
    subscriber_.close();
    controller_.close();
    context_.close();
    
    controller_.recv(&ctrl);