每个线程的免费 TLS 指针

mac*_*ler 6 c winapi multithreading thread-local-storage

在 MSDN 中阅读以下页面:

在动态链接库中使用线程本地存储

我无法理解在FreeLibrary()调用的情况下谁负责释放每个线程的 TLS 插槽指向的内存。

据我了解,如果我有多个正在运行的线程,它们都会在给定索引中的 TLS 插槽内分配内存。当FreeLibrary()被调用时, onlyDLL_PROCESS_DETACH被触发,因此只有收到DLL_PROCESS_DETACH通知的线程才有机会LocalFree()在调用TlsFree()索引之前调用存储在 TLS 插槽中的自己的数据。这会导致所有没有机会调用LocalFree()其数据的其他线程发生内存泄漏,因为它们没有收到DLL_THREAD_DETACH通知。

有人可以解释应该在何时何地释放存储在每个线程的 TLS 插槽中的缓冲区吗?

Rem*_*eau 6

根据您链接到的文档:

当线程终止时,使用 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;
Run Code Online (Sandbox Code Playgroud)

但是,根据DllMain 入口点文档:

当由于 DLL 加载失败、进程终止或调用 FreeLibraryDLL 从进程中卸载 DLL 时,系统不会DLL_THREAD_DETACH 使用进程的各个线程的值调用 DLL 的入口点函数. 仅向 DLL 发送DLL_PROCESS_DETACH 通知。DLL 可以借此机会为 DLL 已知的所有线程清理所有资源

因此,您必须跟踪存储在每线程 TLS 插槽中的指针,以便DLL_THREAD_DETACH处理程序稍后可以释放任何尚未被处理程序释放的指针DLL_PROCESS_DETACH。例如,通过将指针存储在全局线程安全列表中。


更新:另一种解决方案是让DLL_PROCESS_DETACH处理程序枚举正在运行的线程,访问每个线程的 TIB/TEB(线程信息/环境块)结构。NtQueryInformationThread()可用于检索指向线程的 TIB/TEB 的指针。除此之外,TIB/TEB 还包含一个指向线程的 TLS 数组的指针


更新:在 Vista+ 上,另一种解决方案是使用 FLS(光纤本地存储)而不是 TLS(线程本地存储)。该FlsAlloc()函数采用可选的回调。该FlsCallback文件指出:

应用程序定义的函数。如果正在使用 FLS 插槽,则在纤程删除、线程退出和FLS 索引被释放时调用 FlsCallback 。

并且FlsFree()文档指出:

释放 FLS 索引会释放当前进程中所有 FLS 实例的索引。如果相应的 FLS 槽包含非 NULL 值,则释放 FLS 索引还会导致为每个光纤调用关联的回调例程

根据纤维文档:

光纤可以使用光纤本地存储 (FLS) 为每个光纤创建唯一的变量副本。如果没有发生光纤切换,FLS 的行为与线程本地存储完全相同。的FLS函数(FlsAllocFlsFree,  FlsGetValue,和 FlsSetValue)操作与当前线程相关联的FLS。如果线程正在执行一个纤程并且该纤程被切换,则 FLS 也被切换。