Dela via


Använda trådlokal lagring i ett Dynamic-Link-bibliotek

Det här avsnittet visar användningen av en DLL-startpunktsfunktion för att konfigurera ett TLS-index (Thread Local Storage) för att tillhandahålla privat lagring för varje tråd i en flertrådad process.

TLS-indexet lagras i en global variabel, vilket gör det tillgängligt för alla DLL-funktioner. Det här exemplet förutsätter att DLL:ets globala data inte delas, eftersom TLS-indexet inte nödvändigtvis är detsamma för varje process som läser in DLL:en.

Startpunktsfunktionen använder funktionen TlsAlloc för att allokera ett TLS-index när en process läser in DLL-filen. Varje tråd kan sedan använda det här indexet för att lagra en pekare i sitt eget minnesblock.

När startpunktsfunktionen anropas med värdet DLL_PROCESS_ATTACH utför koden följande åtgärder:

  1. Använder funktionen TlsAlloc för att allokera ett TLS-index.
  2. Allokerar ett minnesblock som ska användas uteslutande av den första tråden i processen.
  3. Använder TLS-indexet i ett anrop till funktionen TlsSetValue för att lagra adressen till minnesblocket i TLS-facket som är associerat med indexet.

Varje gång processen skapar en ny tråd anropas startpunktsfunktionen med värdet DLL_THREAD_ATTACH. Startpunktsfunktionen allokerar sedan ett minnesblock för den nya tråden och lagrar en pekare till den med hjälp av TLS-indexet.

När en funktion kräver åtkomst till de data som är associerade med ett TLS-index anger du indexet i ett anrop till funktionen TlsGetValue. Detta hämtar innehållet i TLS-facket för den anropande tråden, vilket i det här fallet är en pekare till minnesblocket för data. När en process använder laddningstidslänkning med den här DLL-filen, räcker det med startpunktsfunktionen för att hantera den trådspecifika lagringen. Problem kan uppstå med en process som använder körningslänkning eftersom startpunktsfunktionen inte anropas för trådar som finns innan funktionen LoadLibrary anropas, så TLS-minne allokeras inte för dessa trådar. Det här exemplet löser det här problemet genom att kontrollera värdet som returneras av funktionen TlsGetValue och allokera minne om värdet anger att TLS-facket för den här tråden inte har angetts.

När varje tråd inte längre behöver använda ett TLS-index måste den frigöra det minne vars pekare lagras i TLS-facket. När alla trådar har slutförts med ett TLS-index använder du funktionen TlsFree för att frigöra indexet.

När en tråd avslutas anropas startpunktsfunktionen med värdet DLL_THREAD_DETACH och minnet för tråden frigörs. När en process avslutas anropas startpunktsfunktionen med värdet DLL_PROCESS_DETACH och det minne som pekaren refererar till i TLS-indexet frigörs.

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

Följande kod visar användningen av DLL-funktionerna som definierades i föregående exempel.

#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 Biblioteksdata

Med lokal trådlagring