Использование локального хранилища потока в библиотеке Dynamic-Link

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

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

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

При вызове функции точки входа со значением DLL_PROCESS_ATTACH код выполняет следующие действия:

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

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

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

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

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

// The DLL code

#include <windows.h>

static DWORD dwTlsIndex; // address of shared memory
 
// DllMain() is the entry-point function for this DLL. 
 
BOOL WINAPI DllMain(HINSTANCE hinstDLL, // DLL module handle
    DWORD fdwReason,                    // reason called
    LPVOID lpvReserved)                 // reserved
{ 
    LPVOID lpvData; 
    BOOL fIgnore; 
 
    switch (fdwReason) 
    { 
        // The DLL is loading due to process 
        // initialization or a call to LoadLibrary. 
 
        case DLL_PROCESS_ATTACH: 
 
            // Allocate a TLS index.
 
            if ((dwTlsIndex = TlsAlloc()) == TLS_OUT_OF_INDEXES) 
                return FALSE; 
 
            // No break: Initialize the index for first thread.
 
        // The attached process creates a new thread. 
 
        case DLL_THREAD_ATTACH: 
 
            // Initialize the TLS index for this thread.
 
            lpvData = (LPVOID) LocalAlloc(LPTR, 256); 
            if (lpvData != NULL) 
                fIgnore = TlsSetValue(dwTlsIndex, lpvData); 
 
            break; 
 
        // The thread of the attached process terminates.
 
        case DLL_THREAD_DETACH: 
 
            // Release the allocated memory for this thread.
 
            lpvData = TlsGetValue(dwTlsIndex); 
            if (lpvData != NULL) 
                LocalFree((HLOCAL) lpvData); 
 
            break; 
 
        // DLL unload due to process termination or FreeLibrary. 
 
        case DLL_PROCESS_DETACH: 
 
            // Release the allocated memory for this thread.
 
            lpvData = TlsGetValue(dwTlsIndex); 
            if (lpvData != NULL) 
                LocalFree((HLOCAL) lpvData); 
 
            // Release the TLS index.
 
            TlsFree(dwTlsIndex); 
            break; 
 
        default: 
            break; 
    } 
 
    return TRUE; 
    UNREFERENCED_PARAMETER(hinstDLL); 
    UNREFERENCED_PARAMETER(lpvReserved); 
}

// The export mechanism used here is the __declspec(export)
// method supported by Microsoft Visual Studio, but any
// other export method supported by your development
// environment may be substituted.

#ifdef __cplusplus    // If used by C++ code, 
extern "C" {          // we need to export the C interface
#endif

__declspec(dllexport)
BOOL WINAPI StoreData(DWORD dw)
{
   LPVOID lpvData; 
   DWORD * pData;  // The stored memory pointer 

   lpvData = TlsGetValue(dwTlsIndex); 
   if (lpvData == NULL)
   {
      lpvData = (LPVOID) LocalAlloc(LPTR, 256); 
      if (lpvData == NULL) 
         return FALSE;
      if (!TlsSetValue(dwTlsIndex, lpvData))
         return FALSE;
   }

   pData = (DWORD *) lpvData; // Cast to my data type.
   // In this example, it is only a pointer to a DWORD
   // but it can be a structure pointer to contain more complicated data.

   (*pData) = dw;
   return TRUE;
}

__declspec(dllexport)
BOOL WINAPI GetData(DWORD *pdw)
{
   LPVOID lpvData; 
   DWORD * pData;  // The stored memory pointer 

   lpvData = TlsGetValue(dwTlsIndex); 
   if (lpvData == NULL)
      return FALSE;

   pData = (DWORD *) lpvData;
   (*pdw) = (*pData);
   return TRUE;
}
#ifdef __cplusplus
}
#endif

В следующем коде показано использование функций DLL, определенных в предыдущем примере.

#include <windows.h> 
#include <stdio.h> 
 
#define THREADCOUNT 4 
#define DLL_NAME TEXT("testdll")

VOID ErrorExit(LPSTR); 

extern "C" BOOL WINAPI StoreData(DWORD dw);
extern "C" BOOL WINAPI GetData(DWORD *pdw);
 
DWORD WINAPI ThreadFunc(VOID) 
{   
   int i;

   if(!StoreData(GetCurrentThreadId()))
      ErrorExit("StoreData error");

   for(i=0; i<THREADCOUNT; i++)
   {
      DWORD dwOut;
      if(!GetData(&dwOut))
         ErrorExit("GetData error");
      if( dwOut != GetCurrentThreadId())
         printf("thread %d: data is incorrect (%d)\n", GetCurrentThreadId(), dwOut);
      else printf("thread %d: data is correct\n", GetCurrentThreadId());
      Sleep(0);
   }
   return 0; 
} 
 
int main(VOID) 
{ 
   DWORD IDThread; 
   HANDLE hThread[THREADCOUNT]; 
   int i; 
   HMODULE hm;
 
// Load the DLL

   hm = LoadLibrary(DLL_NAME);
   if(!hm)
   {
      ErrorExit("DLL failed to load");
   }

// Create multiple threads. 
 
   for (i = 0; i < THREADCOUNT; i++) 
   { 
      hThread[i] = CreateThread(NULL, // default security attributes 
         0,                           // use default stack size 
         (LPTHREAD_START_ROUTINE) ThreadFunc, // thread function 
         NULL,                    // no thread function argument 
         0,                       // use default creation flags 
         &IDThread);              // returns thread identifier 
 
   // Check the return value for success. 
      if (hThread[i] == NULL) 
         ErrorExit("CreateThread error\n"); 
   } 
 
   WaitForMultipleObjects(THREADCOUNT, hThread, TRUE, INFINITE); 

   FreeLibrary(hm);
 
   return 0; 
} 
 
VOID ErrorExit (LPSTR lpszMessage) 
{ 
   fprintf(stderr, "%s\n", lpszMessage); 
   ExitProcess(0); 
}

Данные библиотеки динамической компоновки

Использование локального хранилища потоков