每个线程的免费 TLS 指针

Free TLS pointers for every thread

正在阅读 MSDN 中的以下页面:

Using Thread Local Storage in a Dynamic-Link Library

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

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

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

根据您链接到的文档:

When a thread terminates, the entry-point function is called with the DLL_THREAD_DETACH value and the memory for that thread is freed.

这是释放 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 文档:

When a DLL is unloaded from a process as a result of an unsuccessful load of the DLL, termination of the process, or a call to FreeLibrary, the system does not call the DLL's entry-point function with the DLL_THREAD_DETACH value for the individual threads of the process. The DLL is only sent a DLL_PROCESS_DETACH notification. DLLs can take this opportunity to clean up all resources for all threads known to the DLL.

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


更新:另一种解决方案是让 DLL_PROCESS_DETACH 处理程序枚举 运行 线程,访问 TIB/TEB(线程 Information/Environment Block) 每个线程的结构。 NtQueryInformationThread() can be used to retrieve a pointer to a thread's TIB/TEB. Amongst other things, the TIB/TEB also contains a pointer to the thread's TLS array.


更新:在 Vista+ 上,另一种解决方案是使用 FLS(光纤本地存储)而不是 TLS(线程本地存储)。 FlsAlloc() function takes an optional callback. The FlsCallback 文档指出:

An application-defined function. If the FLS slot is in use, FlsCallback is called on fiber deletion, thread exit, and when an FLS index is freed.

FlsFree() 文档指出:

Freeing an FLS index frees the index for all instances of FLS in the current process. Freeing an FLS index also causes the associated callback routine to be called for each fiber, if the corresponding FLS slot contains a non-NULL value.

根据 Fibers 文档:

A fiber can use fiber local storage (FLS) to create a unique copy of a variable for each fiber. If no fiber switching occurs, FLS acts exactly the same as thread local storage. The FLS functions (FlsAlloc, FlsFreeFlsGetValue, and FlsSetValue) manipulate the FLS associated with the current thread. If the thread is executing a fiber and the fiber is switched, the FLS is also switched.