c++ - 线程对象的 WaitForSingleObject 在 DLL 卸载中不起作用

标签 c++ multithreading winapi dll

当卸载 DLL 时,我偶然发现了 Windows 线程机制的意外行为。 A 有一组工作线程对象,我试图在卸载 DLL 时(通过 DllMain DLL_PROCESS_DETACH)优雅地完成它们。代码非常简单(我确实发送了一个事件来完成线程的等待循环):

WaitForSingleObject( ThrHandle, INFINITE );
CloseHandle( ThrHandle );

然而 WaitForSingleObject 挂起了整个事情。如果我在卸载 DLL 之前执行它,它工作正常。如何修复此行为?

最佳答案

You can't wait for a thread to exit in DllMain().除非在收到 DLL_PROCESS_DETACH 时线程已经退出,否则这样做将总是死锁。这是预期的行为。

这样做的原因是对 DllMain() 的调用是通过加载器锁序列化的。当调用 ExitThread() 时,它要求加载程序锁,以便它可以使用 DLL_THREAD_DETACH 调用 DllMain()。在该调用完成之前,线程仍在运行。

所以DllMain在等待线程退出,线程在等待DllMain退出,典型的死锁情况。

另见 Dynamic-Link Library Best Practices在 MSDN 上。

解决方案是在您的 DLL 中添加一个新函数,供应用程序在卸载 DLL 之前调用。正如您所注意到的,您的代码在显式调用时已经运行良好。


如果向后兼容性要求无法添加这样的函数,并且如果您必须拥有工作线程,请考虑将您的 DLL 拆分为两部分,其中一部分由另一部分动态加载.动态加载的部分将包含(至少)工作线程所需的所有代码。

当由应用程序本身加载的 DLL 接收到 DLL_PROCESS_DETACH 时,您只需设置事件以指示线程退出,然后立即返回。必须指定其中一个线程等待所有其他线程,然后释放第二个 DLL,您可以使用 FreeLibraryAndExitThread() 安全地执行此操作。 .

(根据具体情况,特别是如果工作线程正在退出和/或作为常规操作的一部分正在创建新工作线程,您可能需要非常小心以避免竞争条件和/或死锁;这可能是如果您使用线程池和回调而不是手动创建工作线程,则更简单。)


在特殊情况下,线程除了最简单的 Windows API 外不需要使用任何其他 API,可能可以使用线程池和工作回调来避免需要第二个 DLL .回调退出后,您可以使用 WaitForThreadpoolWorkCallbacks() 进行检查,卸载库是安全的——您不需要等待线程本身退出。

这里的问题是回调必须避免任何可能获取加载器锁的 Windows API。没有记录在这方面哪些 API 调用是安全的,并且它因 Windows 的不同版本而异。如果您要调用比 SetEvent 或 WriteFile 更复杂的任何东西,或者如果您使用的是库而不是 native Windows API 函数,则您不得使用此方法。

关于c++ - 线程对象的 WaitForSingleObject 在 DLL 卸载中不起作用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35828004/

相关文章:

c++ - CFile::osNoBuffer 标志在写入文件时导致异常

c++ - Python逐行从子进程捕获stdout

c++ - 硬件功能的包装器

Java - 停止访问数据库的长时间运行的线程

java - 这段代码多线程安全吗?

python - Ctypes 优缺点

c++ - List sort() vs own sort() 时差

c++ - 将 24 位 2 的补语(MSB 在前)值转换为电压

c++ - WriteFile 返回错误代码 995

windows - 如何让显示器处于待机状态而不让它醒来