Dynamic-Link 라이브러리에서 스레드 로컬 스토리지 사용
이 섹션에서는 DLL 진입점 함수를 사용하여 다중 스레드 프로세스의 각 스레드에 대한 프라이빗 스토리지를 제공하도록 TLS(스레드 로컬 스토리지) 인덱스를 설정하는 방법을 보여 줍니다.
TLS 인덱스는 전역 변수에 저장되므로 모든 DLL 함수에서 사용할 수 있습니다. 이 예제에서는 TLS 인덱스가 DLL을 로드하는 각 프로세스에 대해 반드시 동일하지는 않기 때문에 DLL의 전역 데이터가 공유되지 않는다고 가정합니다.
진입점 함수는 TlsAlloc 함수를 사용하여 프로세스가 DLL을 로드할 때마다 TLS 인덱스를 할당합니다. 그런 다음 각 스레드는 이 인덱스 를 사용하여 자체 메모리 블록에 대한 포인터를 저장할 수 있습니다.
진입점 함수가 DLL_PROCESS_ATTACH 값으로 호출되면 코드는 다음 작업을 수행합니다.
- TlsAlloc 함수를 사용하여 TLS 인덱스를 할당합니다.
- 프로세스의 초기 스레드에서만 사용할 메모리 블록을 할당합니다.
- TlsSetValue 함수 호출에서 TLS 인덱스를 사용하여 인덱스와 연결된 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);
}
관련 항목