Использование локальной памяти потока (TLS) в динамически подключаемых библиотеках (DLL)


Этот раздел показывает использование функции точки входа в DLL, чтобы установить индекс локальной памяти потока (TLS), который предоставит отдельное сохранение данных для каждого потока в многопоточном процессе.

Функция точки входа использует функцию TlsAlloc, чтобы назначать индекс TLS всякий раз, когда процесс загружает DLL. Каждый поток может тогда использовать этот индекс, чтобы сохранить указатель на свой собственный блок памяти.

Когда функция точки входа вызывается со значением DLL_PROCESS_ATTACH, код выполняет ниже перечисленые действия:

  1. Использует функцию TlsAlloc, чтобы назначить индекс TLS.
  2. Назначает блок памяти, который используется исключительно начальным потоком процесса.
  3. Использует индекс TLS при вызове функции TlsSetValue, чтобы сохранить указатель на распределенную память.

Каждый раз, когда процесс создает новый поток, функция точки входа вызывается со значением DLL_THREAD_ATTACH. Функция точки входа тогда назначает блок памяти для нового потока и сохраняет указатель на него, используя индекс TLS. Каждый поток может использовать индекс TLS при вызове TlsGetValue, чтобы извлечь данные об указателе на свой собственный блок памяти.

Когда  поток завершает работу, функция точки входа вызывается со значением DLL_THREAD_DETACH и память для этого потока освобождается. Когда завершает работу процесс, функция точки входа вызывается со значением DLL_PROCESS_DETACH и память, на которую ссылается указатель в индексе TLS освобождается.

Индекс TLS сохраняется в глобальной переменной, делая ее доступный для всех функций DLL. В примере ниже предполагается, что общие данные DLL совместно не используются, потому что индекс TLS - не обязательно один и тот же для каждого процесса, который загружает DLL.

static DWORD dwTlsIndex; // адрес разделяемой памяти
 
// DllMain()- это функция точки входа в эту DLL. 
 
BOOL DllMain(HINSTANCE hinstDLL,  // дескриптор модуля DLL
    DWORD fdwReason,              // причина вызова
    LPVOID lpvReserved)           // зарезервирован
{ 
    LPVOID lpvData; 
    BOOL fIgnore; 
 
    switch (fdwReason) 
    { 
        // Эта DLL загружается благодаря процессу 
        // инициализации или вызову LoadLibrary. 
 
        case DLL_PROCESS_ATTACH: 
 
            // Назначаем индекс TLS.
 
            if ((dwTlsIndex = TlsAlloc()) == 0xFFFFFFFF) 
                return FALSE; 
 
            // Не прерываем: Инициализация индекса для первого потока.
 
        // Связанный процесс создает новый поток. 
 
        case DLL_THREAD_ATTACH: 
 
            // Инициализация индекса TLS для этого потока.
 
            lpvData = (LPVOID) LocalAlloc(LPTR, 256); 
            if (lpvData != NULL) 
                fIgnore = TlsSetValue(dwTlsIndex, lpvData); 
 
            break; 
 
        // Поток связанного процесса завершает работу.
 
        case DLL_THREAD_DETACH: 
 
            // Освобождаем распределенную память для этого потока.
 
            lpvData = TlsGetValue(dwTlsIndex); 
            if (lpvData != NULL) 
                LocalFree((HLOCAL) lpvData); 
 
            break; 
 
        // DLL выгружаtтcя благодаря завершению работы процесса или FreeLibrary. 
 
        case DLL_PROCESS_DETACH: 
 
            // Освобождаем распределенную память для этого потока.
 
            lpvData = TlsGetValue(dwTlsIndex); 
            if (lpvData != NULL) 
                LocalFree((HLOCAL) lpvData); 
 
            // Освобождаем индекс TLS.
 
            TlsFree(dwTlsIndex); 
            break; 
 
        default: 
            break; 
    } 
 
    return TRUE; 
    UNREFERENCED_PARAMETER(hinstDLL); 
    UNREFERENCED_PARAMETER(lpvReserved); 
} 

 

Когда процесс использует выполняемое при загрузке связывание с этой DLL, функции точки входа достаточно, чтобы управлять локальной памятью потока (TLS). Проблемы могут встретиться у процесса, который использует связывание периода выполнения, потому что функция точки входа не вызывалась для потоков, которые существовали до того, как была вызвана функция LoadLibrary, так что память TLS не распределялась для этих потоков. Пример ниже решает эту проблему, проверяя значение, возвращенное функцией TlsGetValue и распределяя память, если значение указывает, что слот TLS для этого потока не установлен.

LPVOID lpvData; 
 
// Извлекаем данные об указателе данных для текущего потока.
 
lpvData = TlsGetValue(dwTlsIndex); 
 
// Если ПУСТО (NULL), назначьте память для этого потока.
 
if (lpvData == NULL) 
{ 
    lpvData = (LPVOID) LocalAlloc(LPTR, 256); 
    if (lpvData != NULL) 
        TlsSetValue(dwTlsIndex, lpvData); 
} 

Назад в оглавление темы
На главную страницу темы

Hosted by uCoz