ZeroMQ 套接字 Recv() 抛出“上下文已终止”异常 - 为啥以及如何恢复?

Posted

技术标签:

【中文标题】ZeroMQ 套接字 Recv() 抛出“上下文已终止”异常 - 为啥以及如何恢复?【英文标题】:ZeroMQ socket Recv() throws 'Context was terminated' exception - why and how to recover?ZeroMQ 套接字 Recv() 抛出“上下文已终止”异常 - 为什么以及如何恢复? 【发布时间】:2011-11-05 18:43:37 【问题描述】:

使用带有 ZeroMQ 的ZMQ.SocketType.REP(回复)消息套接字,我正在接收消息,然后发回“OK”消息。

目前,我正在本地尝试此操作(从在同一台机器上运行的同一 C# 控制台应用程序发送/接收消息)。

相当定期(大约 1500 条消息后),行:

var receivedBytes = _recvSocket.Recv();

... 会抛出异常:Context was terminated

我的问题是,为什么会发生这种情况,您如何从中恢复

我有一个 System.Threading.Thread 专用于运行我的“服务器端”ZeroMQ 回复套接字,这是它运行的循环:

    private static void MessagingLoopReceive(object state)
    
        if (_zmqc == null)
        
            _zmqc = new ZMQ.Context(1);
        

        _recvSocket = _zmqc.Socket(ZMQ.SocketType.REP);
        _recvSocket.Bind("tcp://*:5556");

        while (true)
        
            if (_queueStop)
            
                break;
            

            //Console.WriteLine("Server blocking for receive...");
            var receivedBytes = _recvSocket.Recv();

            if (receivedBytes != null && receivedBytes.Length > 0)
            
                //Console.WriteLine("Server message received from client, sending OK");
                _recvSocket.Send("OK", Encoding.ASCII);
                //Console.WriteLine("Server OK sent, adding message to queue");
                _queuedMessages.Enqueue(receivedBytes);
            
            else
            
                Thread.Sleep(1);
            
        
    

【问题讨论】:

【参考方案1】:

这意味着某人(垃圾收集器?)已经关闭了上下文。

【讨论】:

你是对的 - 上下文的设置方式是 GC 干扰它。谢谢!! 您能详细说明一下吗? 另一种方法是使用GC.KeepAlive:msdn.microsoft.com/en-us/library/… 来防止对对象进行垃圾回收。【参考方案2】:

为了完整起见,添加此答案。

    if (_zmqc == null)
    
        _zmqc = new ZMQ.Context(1);
    
    _recvSocket = _zmqc.Socket(ZMQ.SocketType.REP);

这是唯一使用上下文 _zmqc 的地方。因此,GC 将其视为清理的潜在候选对象。一旦它存在,就会调用它。这将强制所有套接字以来自 ZMQ 的 ETERM 错误号终止。

需要做的第一件事是在上下文需要保持活跃的地方使用 using 子句。这将迫使 GC 不理会对象。

接下来在关闭循环时,您需要捕获异常,看到它是一个 ETERM 错误号,然后优雅地退出。

try

    //Any ZMQ socket sending or receiving 

catch (Exception e)

    if (e.Errno == ETERM)
    
        //Catch a termination error. 
        break; // or return, or end your loop 
    

享受吧!

编辑: 几乎忘记了 ZMQ 中的错误编号是“花哨的”第一个从 156384712 开始并且它们上升了。 ETERM 是 156384712 + 53。但是这只是 doco,因为这可能在编写后发生了变化。

【讨论】:

以上是关于ZeroMQ 套接字 Recv() 抛出“上下文已终止”异常 - 为啥以及如何恢复?的主要内容,如果未能解决你的问题,请参考以下文章

ZeroMQ 在套接字绑定到 ipc:// 协议地址(python)时抛出 ZMQError

ZeroMQ简单介绍

ZeroMQ API 套接字

ZMQ:socket_send/recv 阻塞

如何在 ZeroMQ 中以正确的方式中止 context.socket.recv()?

Recv-Q 和 Send-Q 的使用