在 Dynamic-Link 函式庫中使用線程區域儲存
本節說明如何使用 DLL 進入點函式來設定線程本機記憶體 (TLS) 索引,為多線程進程的每個線程提供私人記憶體。
TLS 索引會儲存在全域變數中,使其可供所有 DLL 函式使用。 此範例假設 DLL 的全域數據不會共享,因為載入 DLL 的每個進程,TLS 索引不一定相同。
進入點函式會使用 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);
}
相關主題