Как освободить объект в TLS-слоте при выходе из потока в Windows?

например, в многопоточной программе:

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, я думаю, не для нормального потока ОС,
и это медленно.

3

Решение

Вместо TlsAllocиспользовать FlsAlloc (и связанные Fls* функции). В FLS вы регистрируете обратный вызов очистки, который ОС будет вызывать в потоке до его завершения, что дает вам возможность очистить.

4

Другие решения

Хорошо. Для Windows Vista и выше, как сказал Джеймс МакНеллис — мы
мог бы использовать FlsCallback.

Для DLL мы могли бы просто использовать DllMain, если параметр причины равен
в DLL_THREAD_DETACH мы делаем очистку. Альтернативой может быть
использовать _pRawDllMain, это так же, как другой DllMain, вы могли бы
найти его от источник повышения.

Для EXE мы могли бы использовать обратный вызов TLS, пожалуйста, посмотрите на
Вот а также Вот, и, конечно же, источник повышения. В
практика, это работает на Windows XP, но я обнаружил, что оптимизации
может сделать его неэффективным, поэтому будьте осторожны с оптимизацией или сделайте
явная ссылка на указатель вашей функции обратного вызова.

Сохраните приведенный ниже код в tls.cpp и добавьте его в свой проект, нет
Неважно, это exe или dll, это будет работать. Обратите внимание, что для DLL на
Windows Vista и выше может вызывать функцию 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")

Если вы думаете, что это неясно, используйте буст at_thread_exit,
сложность скрыта. На самом деле приведенный выше код является упрощенным
версия boost tls. И если вы не хотите использовать boost, на
Система Windows это альтернатива.

Или более общий способ: thread_local.

2

‘Boost :: thread_specific_ptr’ должен работать в любом потоке (согласно ответу на мой вопрос: Проверьте, является ли нить бустерной)

Из-за того, что это медленно, да, это не идеально. Однако вы можете использовать любой обычный механизм TLS, какой пожелаете (я использовал специфический модификатор GCC), а затем создать дополнительный thread_specific_ptr, который очищает данные (создайте оболочку для вашего истинного указателя TLS). Таким образом, создание и удаление TLS немного дороже, но доступ к нему не затрагивается.

1

Вы должны иметь возможность использовать один из многих механизмов выхода из области видимости, например, этот.

Другой альтернативой было бы заключить ваш TLSObject в класс RAII, который освобождает объект при уничтожении оболочки RAII. Это очень распространенный шаблон управления ресурсами, который определенно применим здесь.

0
По вопросам рекламы [email protected]