在MSDN中阅读以下页面:
{{3}}
在FreeLibrary()
调用的情况下,我无法理解谁负责释放每个线程的TLS插槽所指向的内存。
据我所知,如果我有几个正在运行的线程,它们都会在给定索引的TLS槽内分配内存。调用DLL_PROCESS_DETACH
时,仅触发DLL_PROCESS_DETACH
,因此只有收到LocalFree()
通知的线程才有机会在存储在TLS插槽中的自己的数据上调用TlsFree()
,在索引上调用LocalFree()
之前。这导致所有其他线程中的内存泄漏,因为他们没有收到DLL_THREAD_DETACH
通知,因此无法在其数据上调用{{1}}。
有人可以解释在每个线程的TLS插槽中存储缓冲区的时间和位置应该被释放吗?
答案 0 :(得分:4)
根据您链接到的文档:
当一个线程终止时,使用DLL_THREAD_DETACH值调用入口点函数,并释放该线程的内存。
如果线程在终止之前尚未执行此操作,那么应该释放TLS槽指向的内存的理想时间。这在以下提供的示例代码中进行了演示:
case DLL_THREAD_DETACH:
// Release the allocated memory for this thread.
lpvData = TlsGetValue(dwTlsIndex);
if (lpvData != NULL)
LocalFree((HLOCAL) lpvData);
break;
case DLL_PROCESS_DETACH:
// Release the allocated memory for this thread.
lpvData = TlsGetValue(dwTlsIndex);
if (lpvData != NULL)
LocalFree((HLOCAL) lpvData);
// Release the TLS index.
TlsFree(dwTlsIndex);
break;
但是,根据DllMain entry point文档:
如果由于DLL加载失败,终止进程,或调用
FreeLibrary
而从进程中卸载DLL,则系统不会调用DLL的入口点函数,具有进程的各个线程的DLL_THREAD_DETACH
值。该DLL仅发送DLL_PROCESS_DETACH
通知。 DLL可以利用这个机会清理DLL已知的所有线程的所有资源。
因此,您必须跟踪存储在每个线程TLS插槽中的指针,以便DLL_THREAD_DETACH
处理程序之前尚未释放的任何指针可以稍后被{{1}释放处理程序。例如,通过将指针存储在全局线程安全列表中。
更新:另一种解决方案是让DLL_PROCESS_DETACH
处理程序枚举正在运行的线程,访问每个线程的TIB / TEB(线程信息/环境块)结构。 NtQueryInformationThread()
可用于retrieve a pointer to a thread's TIB/TEB。除此之外,the TIB/TEB also contains a pointer to the thread's TLS array。
更新:在Vista +上,另一种解决方案是使用FLS(光纤本地存储)而不是TLS(线程本地存储)。 FlsAlloc()
函数采用可选的回调。 FlsCallback
文档声明:
应用程序定义的函数。如果正在使用FLS插槽,则在光纤删除,线程退出时调用FlsCallback,在释放FLS索引时调用 。
FlsFree()
文档说明:
释放FLS索引可释放当前进程中所有FLS实例的索引。 释放FLS索引还会导致为每条光纤调用关联的回调例程,如果相应的FLS插槽包含非NULL值。
根据Fibers文档:
光纤可以使用光纤本地存储(FLS)为每根光纤创建变量的唯一副本。 如果没有发生光纤切换,FLS的行为与线程本地存储完全相同。 FLS函数(
DLL_PROCESS_DETACH
,FlsAlloc
,FlsFree
和FlsGetValue
)操纵与当前线程相关联的FLS 。如果线程正在执行光纤并且光纤被切换,则FLS也会切换。