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

what is the best way to initialize my global data in shared library?

本文关键字:最佳 方法 是什么 数据 全局 共享 初始化      更新时间:2023-10-16

我有一个单独的类来完成所有需要的初始化。目前我已经声明了这个类类型的全局对象,它在库加载时被实例化。我也见过其他方法,比如声明

BOOL APIENTRY DllMain

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

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

这是在c++ DLL启动时发生的事情:

  1. 系统调用DLL的入口点,由编译器生成
  2. 入口点调用DllMainCRTStartup(名称可能不同),它初始化C/c++运行时并实例化所有全局对象。
  3. DllMainCRTStartup然后调用用户自定义的DllMain。

我个人更喜欢dlmain,因为这样我可以显式地控制初始化的顺序。当您在不同的编译单元中使用全局对象时,它们将按随机顺序初始化,这可能会在截止日期前10分钟带来一些意想不到的惊喜。

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

注:在DllMain中不需要互斥锁,因为对它的所有调用都已经在进程全局临界区下序列化了。也就是说,它保证两个线程不会出于任何目的同时进入它。这也是为什么你不应该与其他线程通信,从这个函数加载其他库等的原因;

在DllMain中不应该做的事情:

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

需要一个静态布尔初始化变量和一个互斥锁。静态初始化"initialized"为0。在你的dlmain()中,调用CreateMutex()。使用bInitialOwner=0和lpName的唯一名称,该名称对您的应用程序来说是唯一的。然后使用WaitForSingleObject()等待互斥对象。检查initialized是否为非零。如果没有,进行初始化,然后将initialized设置为1。如果initialized非零,什么都不做。最后,使用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);
}

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