我有以下代码片段。 context.close() 永远阻塞。有人提示如何解决这个问题吗?我怀疑发生这种情况是因为我发送了一条消息但从未收到答复,因为即使我收到答复, close() 也不会阻止。问题是,如果我没有得到回复怎么办?在这种情况下如何关闭上下文而不导致死锁?我使用 libzmq 4.3.4 和 cppzmq 4.7.1。
#include <zmq.h>
int main()
{
zmq::context_t context(1);
zmq::socket_t socket1(context, zmq::socket_type::req);
const std::string foo("foo_");
socket1.connect("tcp://127.0.0.1:8889");
zmq::message_t msg(foo.size());
std::memcpy(msg.data(), foo.c_str(), foo.size());
socket1.send(std::move(msg), zmq::send_flags::none);
socket1.set(zmq::sockopt::rcvtimeo, 500);
socket1.recv(msg);
socket1.disconnect("tcp://127.0.0.1:8889");
socket1.set(zmq::sockopt::linger, 0);
socket1.close();
context.close();
}
问题1:
“(...)如果我没有得到回复怎么办?”
您的
.recv()
呼叫将按照记录和指示挂起。在所有(实际上是下划线,所有包括所有教科书示例中)信令和消息传递应用程序更喜欢使用:
( a )
一个
Poller.poll( ZMQ_NOBLOCK )
修饰的预测试,如果任何消息已经到达相应的本地主机 Context()
实例,然后调用“pre-ACK-ed” .recv( ZMQ_NOBLOCK )
方法最终确实将数据(从本地主机端Context()
引擎队列存储)“读取”到您的应用程序手中(零复制功能在这里有其价值),或
( b )
类似“事件循环”、预定、盲无块/快/慢读取器循环,其中
.recv( amount_of_waiting )
-调用不会永远挂起并让应用程序逻辑的其他部分继续运行。
如 ZeroMQ 早期版本本机 API 中所述,建议在实例化后通过
ZMQ_LINGER
方法立即设置 .setsockopt()
属性,以防止任何不需要的和间歇性的挂起(在早期版本 v2.0.11 中,我什至记得这种情况) O/S wXP 必须重新启动,因为异常恐慌代码中挂起的上下文孤儿仍然是 llllll,牙齿和指甲,(b)锁定地址/端口处理不当......是的,时间是如此,太快了)。较新的 ZeroMQ 版本添加了另一个选项,用于设置/配置实际的 Context()
实例以进一步实例化 ZMQ_LINGER 已设置为预配置值的 Socket()
实例,因此最好检查您的语言绑定版本、ZermMQ 本机的版本C-API 调用被正确镜像,并在 ZMQ_LINGER
实例开始“使用”之前使用上述任一方法提前设置 Socket()
(无论是先验、尽快还是“足够早”)。没有人愿意“调试”释放锁/活锁的分布式原因,正如 Pieter HINTJENS 和 Martin SUTRIK 多次争论的那样。
Q2 :
“在这种情况下如何关闭上下文而不导致死锁?”
最好永远不要陷入这种矛盾的状态。如果系统地使用,上面的一些策略将为您的代码提供永远不会失去对执行时的控制的优势(几乎软实时事件驱动系统可以以这种方式进行调度 - 在“受控”多路复用中多次使用它”在许多不同优先级的套接字中,“注意”具有严格的“周转时间”要求,以保持对一组远程设备中的许多设备的控制,因此,如果对单独套接字采取类似的照顾,那么一个套接字应该工作得足够好永远不要用未处理的
Socket()
实例挂起上下文,陷入不可恢复的分布式有限状态自动机(dFSA,就像 REQ/REP 一样) - 可能会喜欢其他帖子以查看有关此死锁的更多详细信息,那就是它肯定会发生,只有当它发生时才不确定)。
无论如何,享受零之禅并祝你好运