【问题标题】:How to create threads in dlls (c++)?如何在 dll (c++) 中创建线程?
【发布时间】:2018-07-26 08:22:22
【问题描述】:

我知道我无法从 DllMain 创建线程,但是在 DLL 中创建线程的正确方法是什么?

我试图创建一个环绕它的类并声明该类的全局实例,但随后 WaitForSingleObject 挂在该类的析构函数中,即使我确定线程已退出。

这是我从 exe 加载 dll 的示例程序:

#include <Windows.h>
#include <iostream>

using namespace std;

int main(int argc, TCHAR* argv[])
{
    HMODULE module = LoadLibraryA("dll.dll");
    Sleep(500);
    if (module) FreeModule(module);
    else cout << "failed to load library...\n";
    return 0;
}

这里是dll代码:

#include <Windows.h>
#include <iostream>

using namespace std;

class X
{
    HANDLE thread = NULL;

    static DWORD WINAPI run_thread(LPVOID param)
    {
        cout << "dll.cpp: thread started\n";
        Sleep(1000);
        cout << "dll.cpp: thread is exiting\n";
        return 0;
    }

public:
    X()
    {
        DWORD tmp;
        thread = CreateThread(NULL, 0, run_thread, NULL, 0, &tmp);
        if (thread == NULL)
        {
            cout << "failed to create thread\n";
            throw 1;
        }
    }
    ~X()
    {
        if (thread != NULL)
        {
            cout << "joining thread...\n";
            DWORD ret = WaitForSingleObject(thread, INFINITE);
            cout << "result from waiting: " << ret << "\n";
            thread = NULL;
        }
    }
}x;

BOOL APIENTRY DllMain(HMODULE hModule, DWORD reason, LPVOID lpReserved)
{
    switch (reason)
    {
    case DLL_PROCESS_ATTACH:
        cout << __FUNCTION__ << " reason DLL_PROCESS_ATTACH\n";
        break;
    case DLL_THREAD_ATTACH:
        cout << __FUNCTION__ << " reason DLL_THREAD_ATTACH\n"; 
        break;
    case DLL_THREAD_DETACH:
        cout << __FUNCTION__ << " reason DLL_THREAD_DETACH\n"; 
        break;
    case DLL_PROCESS_DETACH:
        cout << __FUNCTION__ << " reason DLL_PROCESS_DETACH\n"; 
        break;
    }

    return TRUE;
}

这是程序输出:

DllMain reason DLL_PROCESS_ATTACH
DllMain reason DLL_THREAD_ATTACH
dll.cpp: thread started
DllMain reason DLL_PROCESS_DETACH
joining thread...
dll.cpp: thread is exiting
                            <- notice how WaitForSingleObject hangs,
                               even though the thread has exited

这种方法有什么问题?

如果我碰巧从 run_thread 中删除了 Sleep(1000),以便线程在调用 WaitForSingleObject 之前完成,那么它不会挂起:

DllMain reason DLL_PROCESS_ATTACH
DllMain reason DLL_THREAD_ATTACH
dll.cpp: thread started
dll.cpp: thread is exiting
DllMain reason DLL_THREAD_DETACH
DllMain reason DLL_PROCESS_DETACH
joining thread...
result from waiting: 0

我不明白其中的区别,这也不是一种选择,因为线程的目的是在没有发出 DLL_PROCESS_DETACH 信号的情况下不断循环。

【问题讨论】:

  • 程序调用 FreeModule() 而该模块中的代码仍在忙于执行。行为未定义。

标签: c++ windows multithreading dll


【解决方案1】:

我知道我无法从 DllMain 创建线程,但是在 DLL 中创建线程的正确方法是什么?

在主应用程序加载 dll(通过LoadLibrary 隐式或显式)加载后,它会调用您的 dll 导出的一些函数,该函数可以做任何它喜欢的事情,包括启动线程。

我尝试创建一个环绕它的类并声明该类的全局实例

全局对象的初始化(销毁)也发生在DllMain 内部; “真正的”DllMain 入口点(这是 MSDN 谈到的一个)由 CRT 获取,CRT 使用它来初始化(销毁)globals & co。 然后调用你的DllMain

this answer 中针对类似的问题(关于LoadLibrary 而不是CreateThread,但其中大部分也适用于这种情况)您可以找到更多详细信息。

这种方法有什么问题?

你试图在DllMain 中做复杂的事情是错误的,尤其是explicitly discouraged 的事情;理想情况下,DllMain 应该是空的或者做极其琐碎的工作。

很可能,在这种特定情况下,被终止的线程正在尝试再次调用DllMain 以获取DLL_THREAD_DETACH 通知,但由于存在阻止并发访问DllMain 的隐式互斥锁而被锁定,或者因为 Windows 明确禁止 DLL_THREAD_DETACH 通知在 DLL_PROCESS_DETACH 之后到达。

这不会让我感到惊讶 - 毕竟,正如 Hans Passant 所指出的,在代码仍在运行的情况下调用 FreeLibrary 显然是未定义的行为。不可能有一个特定的函数 (FreeLibraryAndExitThread) 让持有对库的引用的线程原子地释放库并杀死自己。

【讨论】:

  • 在为DLL_PROCESS_DETACH 持有加载器锁的同时,析构函数在线程上等待,但在退出时线程调用LdrShutdownThread,它需要DLL_THREAD_DETACH 的加载器锁。这是一个简单的死锁。
  • @eryksun:好的,这是“最有可能”段落中的第一个假设。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-06-05
相关资源
最近更新 更多