4

例如,在多线程程序中:

struct TLSObject;

void foo()
{
    TLSObject* p = TlsGetValue(slot);
    if (p == 0) {
        p = new TLSObject;
        TlsSetValue(slot, p);
    }
    // doing something with p
}

第一次在任何线程中调用 foo() 将生成一个新的 TLSObject。

我的问题是:如何删除 TLSObject(如果我不使用 boost::thread 和 boost::thread_specific_ptr)?

boost::thread_specific_ptr 可以在线程退出时进行清理工作,但我猜它取决于 boost::thread,而不是普通的操作系统线程,而且速度很慢。

4

4 回答 4

5

而不是TlsAlloc,使用FlsAlloc(和相关的Fls*功能)。使用 FLS,您注册一个清理回调,操作系统将在线程终止之前调用该线程,让您有机会进行清理。

于 2013-09-06T06:31:56.893 回答
4

好吧。对于 Windows Vista 及更高版本,正如 James McNellis 所说 - 我们可以使用FlsCallback

对于 DLL,我们可以只使用 DllMain,如果原因参数等于 DLL_THREAD_DETACH,我们会进行清理。另一种方法可能是使用 _pRawDllMain,它就像另一个 DllMain,您可以从boost source中找到它。

对于 EXE,我们可以使用 TLS 回调,请查看 此处此处,当然还有boost source。实际上,它可以在 Windows XP 上运行,但我发现优化可能会使其无效,所以要小心优化,或者明确引用回调函数的指针。

将下面的代码保存到tls.cpp并添加到你的项目中,不管是exe还是dll都可以。请注意,对于 Windows Vista 及更高版本上的 DLL,onThreadExit 函数可能会被调用两次——一次来自 dll_callback,一次来自 tls_callback。

#include <windows.h>

extern void onThreadExit();

static void NTAPI tls_callback(PVOID, DWORD reason, PVOID)
{
    if (reason == DLL_THREAD_DETACH) {
        onThreadExit();
    }
}

static BOOL WINAPI dll_callback(LPVOID, DWORD reason, LPVOID)
{
    if (reason == DLL_THREAD_DETACH) {
        onThreadExit();
    }
    return TRUE;
}

#pragma section(".CRT$XLY",long,read)
extern "C" __declspec(allocate(".CRT$XLY")) PIMAGE_TLS_CALLBACK _xl_y = tls_callback;

extern "C"
{
    extern BOOL (WINAPI * const _pRawDllMain)(HANDLE, DWORD, LPVOID) = &dll_callback;
}

#pragma comment(linker, "/INCLUDE:__tls_used")
#pragma comment(linker, "/INCLUDE:__xl_y")

如果你觉得它晦涩难懂,请使用 boost 的at_thread_exit,复杂性被隐藏了。事实上,上面的代码是 boost tls 的简化版本。如果您不想使用 boost,在 Windows 系统上这是一个替代方案。

或者,更通用的方式:thread_local

于 2015-09-14T16:50:01.763 回答
1

'boost::thread_specific_ptr' 应该在任何线程上工作(根据我的问题的答案:检查线程是否是增强线程

关于它很慢,是的,它并不理想。但是,您可以做的是使用您希望的任何常规 TLS 机制(我使用了 GCC 特定修饰符),然后创建一个额外的 thread_specific_ptr 来清理数据(为您的真正 TLS 指针创建一个包装器)。因此 TLS 的创建和删除有点昂贵,但访问不受影响。

于 2013-09-06T06:22:50.087 回答
0

您应该能够使用许多范围退出机制之一来实现这一点,例如this

另一种选择是将您的 TLSObject 包装到一个 RAII 类中,该类在 RAII 包装器被销毁时释放该对象。这是一种非常常见的资源管理模式,在这里绝对适用。

于 2013-09-06T06:08:23.510 回答