multithreading - DLL_PROCESS_DETACH 只剩下一个线程
问题描述
- - 症状
Dll_PROCESS_DETACH
当我从主机应用程序的子线程加载我的 Dll 并关闭主机应用程序时,调用时只剩下 1 个线程。这是不好的。它会导致内存泄漏并且无法完成所需的清理。
当我从主机应用程序的主线程加载我的 Dll 并关闭主机应用程序时,在 Dll 中创建的所有线程在Dll_PROCESS_DETACH
调用时仍在运行。这很好,因为我可以完成所有需要的清理工作。
我Dll_PROCESS_ATTACH
的没有代码。没有创建线程,没有调用 API 函数。
-- 这个 Dll 的用途,用例
我需要一个可以在各种主机应用程序中运行的 Dll,我不知道我的 Dll 何时加载和卸载。
其中一些主机应用程序显然从一个线程中加载了我的 Dll,例如,该线程正在运行一个脚本,并且该脚本使用了我的 Dll 的导出函数。
一般问题是:当第一次从宿主应用程序的子线程加载DLL时,它没有正确卸载,因为Dll_PROCESS_DETACH
调用时似乎所有线程都被删除了。这不仅会导致内存泄漏,而且它不能做一些内部清理工作,停止线程并进行最终的套接字连接,用于与服务器通信。
当 Dll 从主线程(在我的测试主机中)或在它测试的特定主机应用程序中加载时,所有这些都可以正常工作。
在 Visual Studio 17 中运行的 c++ Dll 的调试会话的两个堆栈跟踪。第一个是坏的,其中 Dll 从子线程加载。
第二个是好的,从主线程加载 Dll。
// exit stack Dll Dll loaded in subthread ; breakpoint in `Dll_PROCESS_DETACH` This is BAD
DllTest.dll!DllTest_app::~DllTest_app() Line 176 C++
[External Code]
DllTest.dll!DllTest_app::destroy() Line 208 C++
DllTest.dll!DllMain(HINSTANCE__ * hModule, unsigned long ul_reason_for_call, void * lpReserved) Line 43 C++
[External Code]
DllTest_test.exe!exit_or_terminate_process(const unsigned int return_code) Line 130 C++
DllTest_test.exe!common_exit(const int return_code, const _crt_exit_cleanup_mode cleanup_mode, const _crt_exit_return_mode return_mode) Line 271 C++
DllTest_test.exe!exit(int return_code) Line 283 C++
[External Code]
// exit stack Dll loaded in mainthread; breakpoint in `Dll_PROCESS_DETACH` This is GOOD
DllTest.dll!DllTest_app::~DllTest_app() Line 175 C++
[External Code]
DllTest.dll!DllTest_app::destroy() Line 208 C++
DllTest.dll!DllMain(HINSTANCE__ * hModule, unsigned long ul_reason_for_call, void * lpReserved) Line 43 C++
[External Code]
DllTest_test.exe!DllTestWrap::Unload(int code, int bdeleteerror) Line 139 C++
DllTest_test.exe!DllTestWrap::~DllTestWrap() Line 66 C++
[External Code]
DllTest_test.exe!_execute_onexit_table::__l22::<lambda>() Line 198 C++
DllTest_test.exe!__crt_seh_guarded_call<int>::operator()<void <lambda>(void),int <lambda>(void) & __ptr64,void <lambda>(void) >(__acrt_lock_and_call::__l3::void <lambda>(void) && setup, _execute_onexit_table::__l22::int <lambda>(void) & action, __acrt_lock_and_call::__l4::void <lambda>(void) && cleanup) Line 199 C++
DllTest_test.exe!__acrt_lock_and_call<int <lambda>(void) >(const __acrt_lock_id lock_id, _execute_onexit_table::__l22::int <lambda>(void) && action) Line 882 C++
DllTest_test.exe!_execute_onexit_table(_onexit_table_t * table) Line 222 C++
DllTest_test.exe!common_exit(const int return_code, const _crt_exit_cleanup_mode cleanup_mode, const _crt_exit_return_mode return_mode) Line 211 C++
DllTest_test.exe!exit(int return_code) Line 283 C++
[External Code]
如何实现,在调用 Dll_PROCESS_DETACH 时,无论 Dll 是从主机应用程序的子线程还是主线程加载,在 Dll 中创建的所有线程仍在运行且未终止。
有没有可以通过编译器或链接器设置或解决方法来完成的事情?
预先感谢您的每一个提示。
解决方案
似乎您的应用程序中的线程之一调用 exit() 导致调用 ExitProcess() API - https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-exitprocess
如 ExitProcess 文档中所述,它终止所有线程,然后使用 DLL_PROCESS_DETACH 调用 DllMain()。
如果终止的线程持有一个互斥体,执行 DLL_PROCESS_DETACH 的最后一个线程试图获取该互斥体,则调用 ExitProcess 也可能导致死锁。
推荐阅读
- github - 如何授予可以直接提交到 GitHub 上受保护分支的用户
- clojure - clojure 阶乘解决方案使用范围和应用 fp-oo 练习
- python - 从 DHT11 将数据记录到 CSV 文件中
- c# - 如何从 switch 到 return 结果返回?
- ios - 如何从视图控制器 Segue 到 TableViewCell
- mysql - 如何将流程添加为数据库中的关系,例如捐赠记录等
- three.js - aframe 状态组件 - 将数据从状态传递到组件
- c# - 可选参数必须是引用类型、可空类型或声明为可选参数。该怎么办?
- javascript - 在 jQuery Grid 中单击时无法选中标题复选框
- android - Android Kotlin - 获取年龄计算的数据意图