C++ 如何在Windows上的线程出口处释放TLS插槽中的对象?

C++ 如何在Windows上的线程出口处释放TLS插槽中的对象?,c++,windows,thread-local-storage,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 } 第一次在任何线程中调用foo()将生成一个新的TLSObject 我的问题是: 如何删除TLSObject(

例如,在多线程程序中:

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)

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

例如,您应该能够使用多种作用域退出机制中的一种来实现这一点


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

boost::thread\u specific\u ptr'应该适用于任何线程(根据我问题的答案:)


说它慢,是的,它不理想。但是,您可以使用任何您想要的普通TLS机制(我使用了特定于GCC的修饰符),然后创建一个额外的特定于线程的ptr来清理数据(创建一个真正TLS指针的包装器)。因此,创建和删除TLS的成本有点高,但访问不会受到影响。

使用(和相关的
Fls*
函数)代替
TlsAlloc
。使用FLS,您可以注册一个清理回调,在线程终止之前,操作系统将调用该回调,从而为您提供清理的机会。

好的。对于Windows Vista及以上版本,正如James McNellis所说,我们 我可以用

对于DLL,如果reason参数等于 要分离DLL_线程,我们需要进行清理。另一种选择可能是 使用Prawdellmain,它就像另一个DllMain一样,你可以 从中找到它

对于EXE,我们可以使用TLS回调,请查看 当然,还有。在里面 实际上,它可以在WindowsXP上运行,但我发现优化 可能会使它无效,所以要小心优化,或者 对回调函数指针的显式引用

将下面的代码保存到tls.cpp并将其添加到项目中,否 不管是exe还是dll,它都能工作。请注意,对于 可以调用Windows Vista及更高版本的onThreadExit函数 两次-一次来自dll\U回调,一次来自tls\U回调

#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")
#包括
extern void onThreadExit();
静态无效NTAPI tls_回调(PVOID、DWORD reason、PVOID)
{
如果(原因==DLL\u线程\u分离){
onThreadExit();
}
}
静态BOOL-WINAPI-dll_回调(LPVOID、DWORD-reason、LPVOID)
{
如果(原因==DLL\u线程\u分离){
onThreadExit();
}
返回TRUE;
}
#pragma部分(“.CRT$XLY”,长,读)
外部“C”declspec(allocate(“.CRT$XLY”))PIMAGE\u TLS\u CALLBACK\u xl\u y=TLS\u CALLBACK;
外部“C”
{
外部BOOL(WINAPI*const\u prawdlmain)(HANDLE、DWORD、LPVOID)=&dll\u回调;
}
#pragma注释(链接器,“/INCLUDE:\uu tls\u used”)
#pragma注释(链接器,“/INCLUDE:\uuuuuxl\uy”)
如果您认为它不清楚,请使用boost的 复杂性是隐藏的。事实上,上面的代码是简化的 boost tls的版本。如果您不想使用boost,请打开 Windows系统这是另一种选择


或者,一种更通用的方法:。

范围退出不适用于线程生存期。他希望在线程结束时清理对象,而不是在作用域退出时。我刚才做了一个测试,线程特定的ptr可以与_beginthreadex一起工作,这是个好消息。现在,我使用Win32 TLS进行访问,使用特定于线程的ptr进行清理,它可以正常工作!这个想法太棒了,非常感谢。詹姆斯,我知道你是VC工程师,标准库和VC编译器使用Fls还是Tls?使用Fls代替Tls是否总是安全的(是否确实推荐)?我知道与Tls相比,Fls的插槽非常有限。最新版本的运行库在所有可用的操作系统上都使用Fls(Fls是在Windows Vista中引入的)。有128个FLS插槽可供进程使用(FLS_最大可用)。正如您所说,FLS是在Windows Vista中引入的,那么Windows XP呢?我能有其他选择吗?@amanjiang不,Windows XP已经死了。我不知道有哪种解决方案可以在Windows XP上运行。非常有创意,但需要注意的是:这种方法很脆弱,将来可能会悄无声息地崩溃(因为不会调用tls_回调),它也不能保证回调后不会调用其他CRT代码,这取决于您正在做什么,可能会再次分配TLS。