Verwenden des lokalen Threadspeichers in einer Dynamic-Link-Bibliothek

In diesem Abschnitt wird die Verwendung einer DLL-Einstiegspunktfunktion zum Einrichten eines TLS-Indexes (Local Storage, lokaler Threadspeicher) erläutert, der privaten Speicher für jeden Thread eines Multithreadprozesses bereitstellt.

Der TLS-Index wird in einer globalen Variablen gespeichert, sodass er für alle DLL-Funktionen verfügbar ist. In diesem Beispiel wird davon ausgegangen, dass die globalen Daten der DLL nicht freigegeben werden, da der TLS-Index nicht unbedingt für jeden Prozess identisch ist, der die DLL lädt.

Die Einstiegspunktfunktion verwendet die TlsAlloc-Funktion , um einen TLS-Index zuzuordnen, wenn ein Prozess die DLL lädt. Jeder Thread kann diesen Index dann verwenden, um einen Zeiger auf seinen eigenen Speicherblock zu speichern.

Wenn die Einstiegspunktfunktion mit dem DLL_PROCESS_ATTACH-Wert aufgerufen wird, führt der Code die folgenden Aktionen aus:

  1. Verwendet die TlsAlloc-Funktion , um einen TLS-Index zuzuordnen.
  2. Weist einen Speicherblock zu, der ausschließlich vom anfänglichen Thread des Prozesses verwendet werden soll.
  3. Verwendet den TLS-Index in einem Aufruf der TlsSetValue-Funktion , um die Adresse des Speicherblocks im TLS-Slot zu speichern, der dem Index zugeordnet ist.

Jedes Mal, wenn der Prozess einen neuen Thread erstellt, wird die Einstiegspunktfunktion mit dem DLL_THREAD_ATTACH-Wert aufgerufen. Die Einstiegspunktfunktion weist dann einen Speicherblock für den neuen Thread zu und speichert mithilfe des TLS-Index einen Zeiger darauf.

Wenn eine Funktion Zugriff auf die Daten benötigt, die einem TLS-Index zugeordnet sind, geben Sie den Index in einem Aufruf der TlsGetValue-Funktion an. Dadurch wird der Inhalt des TLS-Steckplatzes für den aufrufenden Thread abgerufen, der in diesem Fall ein Zeiger auf den Speicherblock für die Daten ist. Wenn ein Prozess die Ladezeitverknüpfung mit dieser DLL verwendet, ist die Einstiegspunktfunktion ausreichend, um den lokalen Threadspeicher zu verwalten. Probleme können bei einem Prozess auftreten, der Laufzeitverknüpfung verwendet, da die Einstiegspunktfunktion nicht für Threads aufgerufen wird, die vorhanden sind, bevor die LoadLibrary-Funktion aufgerufen wird, sodass TLS-Arbeitsspeicher nicht für diese Threads zugewiesen wird. In diesem Beispiel wird dieses Problem gelöst, indem der von der TlsGetValue-Funktion zurückgegebene Wert überprüft und Arbeitsspeicher zugewiesen wird, wenn der Wert angibt, dass der TLS-Slot für diesen Thread nicht festgelegt ist.

Wenn jeder Thread keinen TLS-Index mehr verwenden muss, muss er den Speicher freigeben, dessen Zeiger im TLS-Slot gespeichert ist. Wenn alle Threads die Verwendung eines TLS-Index abgeschlossen haben, verwenden Sie die TlsFree-Funktion , um den Index freizugeben.

Wenn ein Thread beendet wird, wird die Einstiegspunktfunktion mit dem wert DLL_THREAD_DETACH aufgerufen, und der Arbeitsspeicher für diesen Thread wird freigegeben. Wenn ein Prozess beendet wird, wird die Einstiegspunktfunktion mit dem DLL_PROCESS_DETACH-Wert aufgerufen, und der Speicher, auf den der Zeiger im TLS-Index verweist, wird freigegeben.

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

Der folgende Code veranschaulicht die Verwendung der im vorherigen Beispiel definierten DLL-Funktionen.

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

Dynamic-Link-Bibliotheksdaten

Verwenden von TLS (threadlokaler Speicher)