【问题标题】:Creating a thread in DllMain?在 DllMain 中创建一个线程?
【发布时间】:2010-12-13 21:11:27
【问题描述】:

似乎当在DLL_PROCESS_ATTACH 上从 DllMain 中创建一个线程时,它不会在所有 dll 都被加载之前开始。因为我需要确保线程在继续之前运行,所以我遇到了死锁。有什么办法可以强制线程启动吗?

【问题讨论】:

    标签: winapi dll multithreading


    【解决方案1】:

    您不应该从 DLLMain 执行任何 API 调用,尤其是对于创建线程或窗口之类的事情。 Raymond Chen 已经写过很多次了。这里是one,特别相关。

    【讨论】:

    • CreateThread 是您可以在 DllMain 中执行的少数几件事之一,因为它是对 kernel32 的调用,保证已经加载。
    • @Ben Voigt 但要格外小心,因为如果当前 DllMain 调用被要求等待从新线程返回,则很容易陷入死锁。
    • @mloskot:在DllMain 中调用等待函数非常糟糕,无论您在等待什么样的对象。通常,任何从 DLL 执行代码的线程都需要对该 DLL 有一个引用计数,而不是从它下面映射出的代码。这可以防止问题发生,因为只要线程正在运行,DllMain 就不会被调用(用于进程分离)。
    • @BenVoigt:虽然CreateThread 可以安全地从DllMain 调用(假设您可以将其挂起并且不调用任何等待函数),但这仍然是一个坏主意,因为正是Raymond 的博文中提到的原因:如果您的 DLL 被其他一些依赖 DLL 短暂加载和卸载,您不希望在这种情况下创建不必要的资源(如线程)。
    • 我相信Does creating a thread from DllMain deadlock or doesn’t it? 更相关。底线:从DllMain 调用CreateThread 是安全的(即使不推荐)。
    【解决方案2】:

    你的线程是做什么的?

    如果您试图将内容移动到第二个线程以避免限制您在 DllMain 中可以执行的操作,那么运气不好。这些不是对 DllMain 可以做什么的限制,它们是对 DllMain 运行时可以做什么的限制(并持有加载程序锁)。如果您的线程需要获取加载程序锁,它将等到第一个线程完成使用它。如果您的线程不需要加载程序锁,我不明白为什么它不能立即继续......但是没有不需要加载程序锁的线程。 Windows 必须在您的线程开始运行之前向所有 DLL 发送 DLL_THREAD_ATTACH 消息,这意味着它还调用您自己的 DllMain,并且 Windows 可以防止重新进入。

    没有办法解决这个问题。线程在 DLL_THREAD_ATTACH 处理之后才能启动,并且当您的第一个线程在 DllMain 中时,这不会发生。唯一可能的解决方法是启动一个新进程(它有一个独立的加载程序锁,不会阻塞等待你的进程)。

    【讨论】:

    • 在处理DLL_PROCESS_ATTACH 消息期间,我尝试了一个简单的练习,即在DLLMain() 中使用CreateThread() 来实现一个非常简单的工作函数。线程函数使用Sleep() 1000 毫秒。一切正常。我可以看到来自执行DLL_PROCESS_ATTACH 的初始线程和在附加处理期间创建的线程的DLL_THREAD_ATTACH 消息和DLL_THREAD_DETACH 消息。然而,这个工作线程除了Sleep() 之外什么也没做。 Kernel32.dll 调用应该没问题。使用 VS 2005 Windows 7 测试。
    • 奇怪的是,这确实是解决所提出问题的唯一答案。我不知道为什么 OP 决定接受一个不是的答案。
    【解决方案3】:

    没有。您不应该从 DllMain 调用 CreateThread(或任何变体)。尝试同步将导致死锁。你到底想做什么?

    Best Practices for Creating DLLs

    【讨论】:

    • 调用 CreateThread 没有问题,但没有办法解决线程将被挂起直到父线程中的 DllMain 处理完成的事实。
    • 来自提供的链接:“你不应该在 DllMain 中执行以下任务:调用 CreateThread。如果你不与其他线程同步,创建一个线程可以工作,但这是有风险的。”跨度>
    【解决方案4】:

    如果你这样做,你就是在自找麻烦。您不应(直接或间接)对 dll 之外的函数(包括 C 库调用等)进行任何调用。

    如果您无法更改您拥有的 DLL(例如,您没有源代码),那么如果您的 DLL 在其余部分之后动态加载,您可能可以摆脱这种情况您的依赖 DLL 已初始化。如果您可以避免这种方法,我不会推荐这种方法,因为找出依赖链并不总是微不足道的(例如,如果您的 dll 导致依赖 dll 通过 COM 或其他方式动态加载第三个)。

    【讨论】:

    • 这里的第一段是一个过度概括。调用你知道的 DLL 是完全没问题的,因为事实已经在你之前加载了。一般来说,在大多数情况下,这意味着只有 KERNEL32.DLL,这听起来像是一个严重的限制,但您可以使用它来引导您摆脱这种情况。
    • 感谢整数诗人。这正是我来这里要解决的问题。
    • @IntegerPoet:kernel32.dllntdll.dll 都保证在其引导代码运行之前被映射到进程的地址空间。
    猜你喜欢
    • 1970-01-01
    • 2017-05-12
    • 1970-01-01
    • 2021-05-19
    • 1970-01-01
    • 2011-02-27
    • 1970-01-01
    • 2022-01-21
    • 2012-03-28
    相关资源
    最近更新 更多