如何在C# Unity中正确释放NetMQ的Socket、Monitor与Poller?
NetMQ在Unity中的资源释放与重连问题解决
问题背景
在Unity环境中使用NetMQ实现Pub/Sub模式,搭配NetMQPoller和NetMQMonitor监控连接状态。初始化代码如下:
m_Publisher = new PublisherSocket(); m_Subscriber = new SubscriberSocket(); m_Poller = new NetMQPoller(); m_SubMonitor = new NetMQMonitor(m_Subscriber, m_InprocSubMonitorAddress, SocketEvents.Connected | SocketEvents.Disconnected); // Events m_SubMonitor.Connected += (s, e) => { Connected = true; UnityMainThreadDispatcher.Instance().Enqueue(() => OnConnected?.Invoke(this, EventArgs.Empty)); }; m_SubMonitor.Disconnected += (s, e) => { Connected = false; UnityMainThreadDispatcher.Instance().Enqueue(() => OnDisconnected?.Invoke(this, EventArgs.Empty)); }; // Monitoring m_SubMonitor.AttachToPoller(m_Poller); m_Poller.RunAsync(); // Subscriber m_Subscriber.Connect($"tcp://{m_IPAddress}:{m_Port}"); // Publisher m_Publisher.Bind($"tcp://*:{m_Port}");
当前释放逻辑如下:
m_SubMonitor.DetachFromPoller(); m_Poller?.Stop(); m_Poller?.Dispose(); m_SubMonitor?.Stop(); m_SubMonitor?.Dispose(); m_Publisher?.Unbind($"tcp://*:{m_Port}"); //m_Publisher?.Close(); m_Publisher?.Dispose(); m_Subscriber?.Disconnect($"tcp://{m_IPAddress}:{m_Port}"); //m_Subscriber?.Close(); m_Subscriber?.Dispose();
问题:尝试连接离线地址后取消连接,重试时会触发**“地址已被占用”**错误,指向inproc://sub.inproc地址及NetMQMonitor创建环节,推测是资源释放顺序或方式错误。同时疑问:每次断开是否需要调用NetMQConfig.Cleanup?目前仅在应用退出时调用一次并传入false避免阻塞是否正确?
正确的资源释放流程
NetMQ的资源释放有严格顺序要求,错误顺序会导致inproc地址残留、线程资源未释放等问题,正确步骤如下:
停止并清理Monitor
先从Poller中分离Monitor,再同步停止Monitor确保内部线程终止,避免资源泄漏:if (m_SubMonitor != null) { m_SubMonitor.DetachFromPoller(); m_SubMonitor.Stop(); m_SubMonitor.Dispose(); m_SubMonitor = null; }停止并清理Poller
停止Poller后需等待异步线程退出,避免后续资源访问异常:if (m_Poller != null) { m_Poller.Stop(); m_Poller.WaitForExit(); m_Poller.Dispose(); m_Poller = null; }清理Socket资源
必须调用Close()处理Socket内部队列和连接,仅调用Dispose()清理不彻底:// 处理Publisher if (m_Publisher != null) { try { m_Publisher.Unbind($"tcp://*:{m_Port}"); } catch (NetMQException) { // 捕获解绑失败异常(如已提前解绑) } m_Publisher.Close(); m_Publisher.Dispose(); m_Publisher = null; } // 处理Subscriber if (m_Subscriber != null) { try { m_Subscriber.Disconnect($"tcp://{m_IPAddress}:{m_Port}"); } catch (NetMQException) { // 捕获断开失败异常(如已提前断开) } m_Subscriber.Close(); m_Subscriber.Dispose(); m_Subscriber = null; }线程安全处理
由于资源在多线程中使用,释放操作需加锁避免并发访问:private readonly object m_Lock = new object(); public void CleanupResources() { lock (m_Lock) { // 上述释放步骤放在此处 } }
关于NetMQConfig.Cleanup的使用
- 不需要每次断开都调用:
NetMQConfig.Cleanup()用于清理NetMQ全局资源(如IO线程、上下文),频繁调用会影响性能,还可能导致后续Socket创建异常。 - 应用退出时调用是正确的:仅在Unity应用退出时调用一次,传入
false避免主线程阻塞,需确保所有Socket、Poller、Monitor已完成释放后再执行:private void OnApplicationQuit() { CleanupResources(); NetMQConfig.Cleanup(false); }
内容的提问来源于stack exchange,提问作者Ecko
相关产品推荐
相关产品推荐

