【问题标题】:what is the best way to initialize my global data in shared library?在共享库中初始化我的全局数据的最佳方法是什么?
【发布时间】:2011-08-12 04:10:48
【问题描述】:

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

BOOL APIENTRY DllMain

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

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

【问题讨论】:

  • 仅供参考 DllMain 可以被多次调用。有多个事件触发此功能。尽管它的名字不是一个真正的“主要”。
  • 请添加 cmets 和一些代码,让我明白你想要什么。

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


【解决方案1】:

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

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

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

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

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

【讨论】:

  • 如果两个进程试图加载库会发生什么?每个都有自己的数据段,对吧?所以全局对象对于每个进程都是唯一的,对吧?
  • 另外值得注意的是,临界区是在 p.1 之前获取的,而不是在 p.3 之前获取的,因此您也不应该从全局对象的构造函数/析构函数中调用 LoadLibrary。
【解决方案2】:

几件不该做的事from DllMain:

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

【讨论】:

  • 可能有用但格式不佳,是否需要将代码块更改为引号或列表?
【解决方案3】:

您需要一个静态布尔初始化变量和一个mutex。将“已初始化”静态初始化为 0。在您的 DllMain() 中,调用 CreateMutex()。使用 bInitialOwner=0 和对您的应用程序唯一的 lpName 的唯一名称。然后使用 WaitForSingleObject() 等待互斥锁。检查初始化是否非零。如果不是,请进行初始化,然后将 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);
}

【讨论】:

  • 我以为初始化是单线程的,那为什么需要互斥锁呢?
  • afaik,Windows 互斥锁对进程是全局的,CriticalSection 在单个进程中为线程工作。
【解决方案4】:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-05-10
    • 2010-09-17
    • 1970-01-01
    • 1970-01-01
    • 2017-11-01
    • 2012-01-28
    • 2010-10-13
    相关资源
    最近更新 更多