Uso di Archiviazione locali di thread in una libreria di Dynamic-Link

Questa sezione illustra l'uso di una funzione del punto di ingresso DLL per configurare un indice di archiviazione locale del thread (TLS) per fornire l'archiviazione privata per ogni thread di un processo multithreading.

L'indice TLS viene archiviato in una variabile globale, rendendolo disponibile per tutte le funzioni DLL. In questo esempio si presuppone che i dati globali della DLL non siano condivisi, perché l'indice TLS non è necessariamente lo stesso per ogni processo che carica la DLL.

La funzione del punto di ingresso usa la funzione TlsAlloc per allocare un indice TLS ogni volta che un processo carica la DLL. Ogni thread può quindi usare questo indice per archiviare un puntatore al proprio blocco di memoria.

Quando la funzione del punto di ingresso viene chiamata con il valore DLL_PROCESS_ATTACH, il codice esegue le azioni seguenti:

  1. Usa la funzione TlsAlloc per allocare un indice TLS.
  2. Alloca un blocco di memoria da usare esclusivamente dal thread iniziale del processo.
  3. Usa l'indice TLS in una chiamata alla funzione TlsSetValue per archiviare l'indirizzo del blocco di memoria nello slot TLS associato all'indice.

Ogni volta che il processo crea un nuovo thread, la funzione del punto di ingresso viene chiamata con il valore DLL_THREAD_ATTACH. La funzione del punto di ingresso alloca quindi un blocco di memoria per il nuovo thread e archivia un puntatore usando l'indice TLS.

Quando una funzione richiede l'accesso ai dati associati a un indice TLS, specificare l'indice in una chiamata alla funzione TlsGetValue . In questo modo viene recuperato il contenuto dello slot TLS per il thread chiamante, che in questo caso è un puntatore al blocco di memoria per i dati. Quando un processo usa il collegamento in fase di caricamento con questa DLL, la funzione del punto di ingresso è sufficiente per gestire l'archiviazione locale del thread. I problemi possono verificarsi con un processo che usa il collegamento in fase di esecuzione perché la funzione del punto di ingresso non viene chiamata per i thread esistenti prima che venga chiamata la funzione LoadLibrary , quindi la memoria TLS non viene allocata per questi thread. Questo esempio risolve questo problema controllando il valore restituito dalla funzione TlsGetValue e allocando memoria se il valore indica che lo slot TLS per questo thread non è impostato.

Quando ogni thread non deve più usare un indice TLS, deve liberare la memoria il cui puntatore viene archiviato nello slot TLS. Al termine dell'uso di un indice TLS, usare la funzione TlsFree per rilasciare l'indice.

Quando termina un thread, la funzione del punto di ingresso viene chiamata con il valore DLL_THREAD_DETACH e la memoria per tale thread viene liberata. Quando termina un processo, la funzione del punto di ingresso viene chiamata con il valore DLL_PROCESS_DETACH e la memoria a cui fa riferimento il puntatore nell'indice TLS viene liberata.

// 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

Il codice seguente illustra l'uso delle funzioni DLL definite nell'esempio precedente.

#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); 
}

Dati della libreria dynamic-link

Uso del Archiviazione locale del thread