Menggunakan Penyimpanan Lokal Utas di Pustaka Dynamic-Link

Bagian ini menunjukkan penggunaan fungsi titik entri DLL untuk menyiapkan indeks penyimpanan lokal utas (TLS) untuk menyediakan penyimpanan privat untuk setiap utas proses multi-terbaca.

Indeks TLS disimpan dalam variabel global, membuatnya tersedia untuk semua fungsi DLL. Contoh ini mengasumsikan bahwa data global DLL tidak dibagikan, karena indeks TLS belum tentu sama untuk setiap proses yang memuat DLL.

Fungsi titik entri menggunakan fungsi TlsAlloc untuk mengalokasikan indeks TLS setiap kali proses memuat DLL. Setiap utas kemudian dapat menggunakan indeks ini untuk menyimpan pointer ke blok memorinya sendiri.

Ketika fungsi titik entri dipanggil dengan nilai DLL_PROCESS_ATTACH, kode melakukan tindakan berikut:

  1. Menggunakan fungsi TlsAlloc untuk mengalokasikan indeks TLS.
  2. Mengalokasikan blok memori yang akan digunakan secara eksklusif oleh utas awal proses.
  3. Menggunakan indeks TLS dalam panggilan ke fungsi TlsSetValue untuk menyimpan alamat blok memori di slot TLS yang terkait dengan indeks.

Setiap kali proses membuat utas baru, fungsi titik entri dipanggil dengan nilai DLL_THREAD_ATTACH. Fungsi titik entri kemudian mengalokasikan blok memori untuk utas baru dan menyimpan penunjuk ke dalamnya dengan menggunakan indeks TLS.

Saat fungsi memerlukan akses ke data yang terkait dengan indeks TLS, tentukan indeks dalam panggilan ke fungsi TlsGetValue . Ini mengambil konten slot TLS untuk utas panggilan, yang dalam hal ini adalah penunjuk ke blok memori untuk data. Ketika proses menggunakan penautan waktu muat dengan DLL ini, fungsi titik entri cukup untuk mengelola penyimpanan lokal utas. Masalah dapat terjadi dengan proses yang menggunakan penautan run-time karena fungsi titik entri tidak dipanggil untuk utas yang ada sebelum fungsi LoadLibrary dipanggil, sehingga memori TLS tidak dialokasikan untuk utas ini. Contoh ini memecahkan masalah ini dengan memeriksa nilai yang dikembalikan oleh fungsi TlsGetValue dan mengalokasikan memori jika nilai menunjukkan bahwa slot TLS untuk utas ini tidak diatur.

Ketika setiap utas tidak perlu lagi menggunakan indeks TLS, itu harus membebaskan memori yang pointer-nya disimpan di slot TLS. Ketika semua utas telah selesai menggunakan indeks TLS, gunakan fungsi TlsFree untuk merilis indeks.

Ketika utas berakhir, fungsi titik entri dipanggil dengan nilai DLL_THREAD_DETACH dan memori untuk utas tersebut dikosongkan. Ketika proses berakhir, fungsi titik entri dipanggil dengan nilai DLL_PROCESS_DETACH dan memori yang dirujuk oleh penunjuk dalam indeks TLS dikosongkan.

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

Kode berikut menunjukkan penggunaan fungsi DLL yang ditentukan dalam contoh sebelumnya.

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

Data Pustaka Dynamic-Link

Menggunakan Penyimpanan Lokal Utas