如何在Windows线程退出时的TLS插槽中释放对象?

ama*_*ang 4 c++ windows thread-local-storage

例如,在多线程程序中:

struct TLSObject;

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

第一次在任何线程中调用foo()都会创建一个新的TLSObject。

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

boost :: thread_specific_ptr可以在线程退出时执行清理工作,但是我想这取决于boost :: thread,不是针对普通的OS线程,而且它很慢。

Jam*_*lis 5

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


ama*_*ang 5

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

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

对于 EXE,我们可以使用 TLS 回调,请查看 此处此处,当然还有boost 源。实际上,它在 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")
Run Code Online (Sandbox Code Playgroud)

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

或者,更通用的方式:thread_local