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