【问题标题】:Free TLS pointers for every thread每个线程的免费 TLS 指针
【发布时间】:2018-01-24 07:47:53
【问题描述】:

在 MSDN 中阅读以下页面:

Using Thread Local Storage in a Dynamic-Link Library

我不明白在FreeLibrary()调用的情况下谁负责释放每个线程的TLS槽指向的内存。

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

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

【问题讨论】:

  • 如果您的 DLL 将数据分配给某个线程 - 在未释放该数据(在线程退出时)之前,不得卸载它。因此,在这种情况下根本不能卸载您的 DLL。或者当您将数据分配给线程时,您需要增加对 DLL 的引用计数。并在释放此数据时减少对 DLL 的引用计数

标签: c multithreading winapi thread-local-storage


【解决方案1】:

根据您链接到的文档:

当线程终止时,使用 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 值的-point 函数,用于进程的各个线程。 DLL 仅发送DLL_PROCESS_DETACH 通知。 DLL 可以借此机会清理 DLL 已知的所有线程的所有资源

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


更新:另一种解决方案是让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 插槽正在使用中,则在删除纤程、线程退出和释放 FLS 索引时调用 FlsCallback。

FlsFree() 文档指出:

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

根据Fibers 文档:

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

【讨论】:

  • 但问题是 - 如果 dll 将被卸载 某些线程(为其分配 tls 数据)终止
  • 是的,你说得对。这(跟踪指针)可能的解决方案之一。
  • Fls 确实是这里最好的解决方案,它表明 Tls 的问题根源不是很好地设计它最初由 windows 功能
  • "如果线程正在执行一个纤程并且切换了纤程,那么 FLS 也会切换。" - 文档中的错字?似乎应该是“并且 thread 已切换”。
  • 不,文档是正确的。您是否甚至阅读了有关纤维实际上是什么的整个文档?一个线程可以运行多个纤程并在它们之间随意切换(这就是纤程的全部意义——手动调度)。因此,当线程在光纤之间切换时,线程的 FLS 也会相应地切换,正如文档所说的那样。
猜你喜欢
  • 2011-04-09
  • 1970-01-01
  • 2021-03-20
  • 2012-12-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多