DLL中提供的ZeroMQ上下文单例在程序退出时崩溃(VS2010 win7 x64 zmq 4.0x)

时间:2013-11-05 17:36:44

标签: c++ zeromq

这是单身人士

#pragma once

class ContextManager {
public:
    static ContextManager& Instance() {
        static ContextManager instance;
        return instance;
    }
    zmq::context_t& GetContext() { return ctx_;}
private:
    zmq::context_t ctx_;
    ~ContextManager() {}
};

我有一个带有一些有用的网络实用程序的DLL,它基于ZeroMQ并使用这个单例不必传递上下文。

我将此DLL链接到运行测试套件的EXE。此测试套件可以工作,发送和接收一些消息。当程序退出时,ContextManager析构函数崩溃,说“断言失败:尚未执行成功的WSASTARTUP(...... \ src \ signaler) 的.cpp:137)

更多详情:

  • 该应用程序是单线程的。
  • 如果我只是从.EXE调用Instance.GetContext()方法并返回(没有测试运行,不再调用DLL接口),那么它也会失败。
  • 如果我在main之前定义这个单例(因此,在不使用DLL中的对象的情况下在exe内部),那么就可以
  • WSastartup只调用一次并且有效。

我不想向DLL客户端公开任何实现细节,所以我想在DLL中包含这个单例。怎么能实现这个目标?

1 个答案:

答案 0 :(得分:4)

问题是ZMQ使用的WinSock需要在使用前调用WSAStartup()。如果您随后调用WSAShutdown()并使用ZMQ,则看起来好像从未调用过WSAStartup(),因此失败的断言。在更抽象的层面上,WSAStartup()和WSAShutdown()之间的时间跨度必须完全包含ZMQ上下文的生命周期。

C ++中的函数级静态是按需创建的,但在main()返回后被销毁(我相信未指定的顺序)。你没有显示对WSAStartup()的调用,但我猜它是在main()中的某个地方。类似地,对WSAShutdown()的调用是在main结束之前,但是在破坏函数静态对象之前仍然会这样做,因此你会遇到问题。

两种可能的修复方法:

  • 使用new分配上下文,永不删除它。您将删除它的唯一一次是程序关闭,在操作系统本身回收程序使用的所有资源之前不久。这是一个简单实用的修复方法。
  • 稍微复杂的是将对WSAStartup()/ WSAShutdown()的调用绑定到单例的ctor / dtor。在ctor中,启动WinSock然后创建上下文。在析构函数中,销毁上下文然后释放WinSock。

您还可以为您的DLL创建两个类似于WSAStartup()和WSAShutdown()的函数,但这样做既不方便又难看。此外,除非绝对必要,否则我至少会考虑不使用单身人士。强制在用户上使用您的代码是一件麻烦事,但这只是我个人的看法。