每个线程的免费TLS指针

时间:2017-08-16 08:45:15

标签: c multithreading winapi thread-local-storage

在MSDN中阅读以下页面:

{{3}}

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

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

有人可以解释在每个线程的TLS插槽中存储缓冲区的时间和位置应该被释放吗?

1 个答案:

答案 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_DETACHFlsAllocFlsFreeFlsGetValue)操纵与当前线程相关联的FLS 。如果线程正在执行光纤并且光纤被切换,则FLS也会切换。