например, в многопоточной программе:
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, я думаю, не для нормального потока ОС,
и это медленно.
Вместо TlsAlloc
использовать FlsAlloc
(и связанные Fls*
функции). В FLS вы регистрируете обратный вызов очистки, который ОС будет вызывать в потоке до его завершения, что дает вам возможность очистить.
Хорошо. Для 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.
‘Boost :: thread_specific_ptr’ должен работать в любом потоке (согласно ответу на мой вопрос: Проверьте, является ли нить бустерной)
Из-за того, что это медленно, да, это не идеально. Однако вы можете использовать любой обычный механизм TLS, какой пожелаете (я использовал специфический модификатор GCC), а затем создать дополнительный thread_specific_ptr, который очищает данные (создайте оболочку для вашего истинного указателя TLS). Таким образом, создание и удаление TLS немного дороже, но доступ к нему не затрагивается.
Вы должны иметь возможность использовать один из многих механизмов выхода из области видимости, например, этот.
Другой альтернативой было бы заключить ваш TLSObject в класс RAII, который освобождает объект при уничтожении оболочки RAII. Это очень распространенный шаблон управления ресурсами, который определенно применим здесь.