Open tnie opened 6 years ago
动态库中子线程被突然掐断(强行结束)! https://github.com/tnie/YDData2/issues/228 https://github.com/tnie/YDData2/issues/227
除非明确主线程等待,否则都会被动态库强行结束?——非全局变量/静态变量的析构都在主线程之内,肯定会等;也只有全局变量、静态变量的析构会在主线程 main()
结束之后
可是如果不是动态库中的全局变量/静态变量,而是主程序中的就不存在被强行结束的困惑。
如果从未意识到上述状况的存在,那么理解下面这段话可能就有些困难:
Consider a DLL that creates worker threads as part of its initialization. Upon DLL cleanup, it is necessary to synchronize with all the worker threads to ensure that the data structures are in a consistent state and then terminate the worker threads.Today, there is no straightforward way to completely solve the problem of cleanly synchronizing and shutting down DLLs in a multithreaded environment. This section describes the current best practices for thread synchronizing during DLL shutdown. 引用来源
由以下描述看出,在 DllMain
中并无有效手段可以用来解决我们的问题。
By the time
DllMain
is called at process exit, all the process’s threads have been forcibly cleaned up and there is a chance that the address space is inconsistent. Synchronization is not required in this case. In other words, the idealDLL_PROCESS_DETACH
handler is empty. 引用来源
问题核心在于 DLL 什么时候卸载,为什么卸载 DLL 并未等其(指 DLL 中的)静态变量析构执行完毕。所以,问题变成了 crt 相关了。
如果动态库(启用线程)析构中不join()
子线程,只是做些耗时操作:打印结果多样,以后者居多
hModule.0F630000 lpReserved.00EFF980
Process attach.
> 主线程开始
> 主线程结束
hModule.0F630000 lpReserved.00000000
Thread attach.
hModule.0F630000 lpReserved.00000001
Process detach.
子线程第0次析构倒计时;
子线程第1次析构倒计时;
子线程第2次析构倒计时;
子线程第3次析构倒计时;
...
子线程第18次析构倒计时;
子线程第19次析构倒计时;
请按任意键继续. . .
以下无 Process detach.
的情形可能源于创建线程后既没有 join()
也未 detach()
hModule.0F6D0000 lpReserved.0098F5B4
Process attach.
> 主线程开始
> 主线程结束
hModule.0F6D0000 lpReserved.00000000
Thread attach.
请按任意键继续. . .
In a Windows C++ DLL, all global objects (including static members of classes) will be constructed just before the calling of the
DllMain
withDLL_PROCESS_ATTACH
, and they will be destroyed just after the call of theDllMain
withDLL_PROCESS_DETACH
. 摘自
答主用了 destroy 而非 destruct,两者等价还是另有玄机?
动态库中的全局变量、静态变量的析构在 DLL_PROCESS_DETACH
之后执行,而采用 Load-Time Dynamic Linking (区别于 Run-Time Dynamic Linking)方式的程序,DLL_PROCESS_DETACH
即意味着进程结束(执行 exist()
?),所以分离的两者如果纠缠的?——会让析构执行到末尾,但也会杀掉所有线程?
If the process is terminating (the lpvReserved parameter is non-NULL), all threads in the process except the current thread either have exited already or have been explicitly terminated by a call to the
ExitProcess
function, which might leave some process resources such as heaps in an inconsistent state. 引用来源
网上有些帖子提到,动态库中全局变量/静态变量的析构函数未被执行,但我实际测试在 DLL 不启用线程时,在 main()
函数 return
之后,静态变量的析构依旧会执行!
能否捕捉线程是执行完毕退出还是被强行终止的?以便进一步确认主线程退出时杀掉了(做了无效托管的)子线程
join after main 是无效的?main() 之外可以有线程吗?
在 dll 静态变量/全局变量的析构函数中指明 join() 的线程,进程退出时会被强行结束——这个到底是不是 msvc 的 bug?https://stackoverflow.com/questions/42535990/worker-threads-are-forcibly-terminated-prior-to-the-destruction-of-static-object
使用除了 msvc2015 之外的编译器测试:
没有解决方案:https://stackoverflow.com/questions/43110740/thread-created-by-static-object-deleted-before-dtor
It seems like you wanted to control the thread shutdown cleanly, did you work out a solution for that? – Phil Brubaker Apr 4 '17 at 1:40 unfortunately, no. The issue is that the thread is created from DLL static object. This object is destructed after the process already cleaned up all its threads. I found two clean, but not comfort solutions, and one comfort but dirty solution – Boris Bolshem Apr 4 '17 at 8:27 1) Clean - make a done() functions, the user of lib should call it before the process ends. Kill a thread in done() function. 2) Clean - User may wrap your object in its own object, and user's object should call done() function in its desctructor 3) Dirty - do not care- the process is finishing, just let it kill all the used resources (in my case it will work) – Boris Bolshem Apr 4 '17 at 8:30
见 feature_thread 分支
如果动态库内线程在卸载动态库(
DLL_THREAD_DETACH
)之前结束:如果线程函数耗时较长,即便代码中做了等待指示,但卸载动态库就好会发生莫名其妙的现象: (等待指示放在动态库全局变量/静态变量的析构函数中,如果是在普通函数(直接或间接被主程序调用,而只要函数调用未结束,就意味着动态库正在使用,不会卸载)就会按部就班等线程函数执行结束)