在共享库中初始化全局数据的最佳方法是什么?

时间:2011-08-12 04:10:49

标签: c++ dll global-variables shared-libraries

我有一个类可以完成所有必需的初始化。 目前我已经声明了这个类类型的全局对象,它正在库加载上实例化。 我已经看过其他方式,比如delaring

BOOL APIENTRY DllMain

共享库的入口点,并在进程附加上进行实际初始化。

这与让隐式全局初始化到它的工作不同吗?哪种方式更好?

4 个答案:

答案 0 :(得分:6)

这是在C ++ DLL启动期间发生的事情:

  1. 系统调用DLL的入口点,由编译器生成
  2. 入口点调用DllMainCRTStartup(名称可能不同),它初始化C / C ++运行时并实例化所有全局对象。
  3. DllMainCRTStartup然后调用用户定义的DllMain。
  4. 我个人更喜欢DllMain,因为这样我可以明确地控制初始化的顺序。当您在不同的编译单元中使用全局对象时,它们将以随机顺序初始化,这可能会在截止日期前10分钟带来一些意外的惊喜。

    DllMain还允许您进行每线程初始化,这是使用全局对象无法实现的。但是,它无法移植到其他平台。

    P.S。您在DllMain中不需要互斥锁,因为对它的所有调用都已在进程全局关键部分下序列化。即保证两个线程不会出于任何目的同时进入它。这也是你不应该与其他线程通信,从这个函数加载其他库等的原因;有关解释,请参阅MSDN article

答案 1 :(得分:2)

一些永远不应该做的事情from DllMain

  • 直接或间接调用LoadLibrary或LoadLibraryEx。这可能会导致死锁或崩溃。
  • 与其他线程同步。这可能会导致死锁。
  • 获取由等待获取加载程序锁定的代码所拥有的同步对象。这可能会导致死锁。
  • 使用CoInitializeEx初始化COM线程。在某些情况下,此函数可以调用LoadLibraryEx。
  • 调用注册表函数。这些功能在Advapi32.dll中实现。如果在DLL之前未初始化Advapi32.dll,则DLL可以访问未初始化的内存并导致进程崩溃。
  • 调用CreateProces。创建进程可以加载另一个DLL。
  • 致电ExitThread。在DLL分离期间退出线程可能导致再次获取加载程序锁定,从而导致死锁或崩溃。
  • 调用CreateThread。如果你不与其他线程同步,那么创建一个线程就可以工作,但是它有风险。
  • 创建命名管道或其他命名对象(仅限Windows 2000)。在Windows 2000中,命名对象由终端服务DLL提供。如果未初始化此DLL,则调用DLL可能导致进程崩溃。
  • 使用动态C运行时(CRT)中的内存管理功能。如果未初始化CRT DLL,则调用这些函数可能会导致进程崩溃。
  • 调用User32.dll或Gdi32.dll中的函数。某些函数会加载另一个DLL,但可能无法初始化。
  • 使用托管代码。

答案 2 :(得分:1)

您需要一个静态布尔初始化变量和一个mutex。将“initialized”静态初始化为0.在DllMain()中,调用CreateMutex()。使用bInitialOwner = 0和lpName的唯一名称,该名称对应用程序是唯一的。然后使用WaitForSingleObject()等待互斥锁。检查初始化是否为非零。如果没有,请进行初始化,然后将初始化设置为1.如果初始化为非零,则不执行任何操作。最后,使用ReleaseMutex()释放互斥锁,并使用CloseHandle()关闭它。

这是一些伪代码,省略了错误和异常处理:

initialized = 0;

DllMain()
{
    mutex = CreateMutex(..., 0, "some-unique-name");
    result = WaitForSingleObject(handle, ...);
    if (result == WAIT_OBJECT_0) {
        if (!initialized) {
            // initialization goes here
            initialized = 1;
        }
    }
    ReleaseMutex(mutex);
    CloseHandle(mutex);
}

答案 3 :(得分:0)

嗨我建议你更喜欢一个signleton类,你只能创建一个类的单个对象并使用它。可以使用私有构造函数创建Sigleton类。现在假设ur class A是一个单例类,它的对象可以在你想要初始化的每个Class的构造函数中使用。请给我们一些示例代码,以便其他人可以帮助您更好。