- 公開視頻 ->?鏈接點擊跳轉公開課程
- 博客首頁 ->????鏈接點擊跳轉博客主頁
目錄
TLS的作用
TLS的實現
靜態 TLS??
動態 TLS??
內部實現
回調機制
TLS Directory 的結構
TLS的作用
-
TLS (Thread Local Storage) 是一種用于為多線程應用程序提供線程獨立存儲空間的機制。在多線程程序中,每個線程可以有自己獨特的一組數據,互不干擾。
-
保存線程狀態數據(如線程上下文)。
-
避免線程之間共享全局變量導致的競爭和沖突。
-
為每個線程提供獨立的緩存、統計等,不需要使用鎖機制。
TLS的實現
靜態 TLS??
在編譯時分配空間(通過 __declspec(thread) 或 thread_local 關鍵字)。
-
通過使用
__declspec(thread)
聲明 TLS 數據。 -
靜態 TLS 在編譯時分配,系統會自動初始化和清理。
-
適用于預定義的 TLS 數據場景。
-
#include <windows.h> #include <stdio.h> // 聲明線程局部存儲變量 __declspec(thread) int tlsData = 0; // 線程函數 DWORD WINAPI ThreadProc(LPVOID lpParameter) { // 為當前線程初始化 TLS 數據 tlsData = (int)(size_t)lpParameter; // 使用 TLS 數據 printf("Thread %d: TLS Data = %d\n", GetCurrentThreadId(), tlsData); // 模擬工作 Sleep(1000); return 0; } int main() { // 創建線程 const int threadCount = 3; HANDLE threads[threadCount]; for (int i = 0; i < threadCount; ++i) { threads[i] = CreateThread(NULL, 0, ThreadProc, (LPVOID)(size_t)(i + 1), 0, NULL); } // 等待線程完成 WaitForMultipleObjects(threadCount, threads, TRUE, INFINITE); // 清理句柄 for (int i = 0; i < threadCount; ++i) { CloseHandle(threads[i]); } return 0; }
動態 TLS??
運行時通過 API(如 TlsAlloc, TlsFree)動態管理。
-
使用
TlsAlloc()
分配一個 TLS 索引,動態管理每個線程的 TLS 數據。 -
每個線程負責分配、獲取和釋放自己的數據。
#include <windows.h>
#include <stdio.h> // 全局 TLS 索引
DWORD g_TlsIndex; // 線程函數
DWORD WINAPI ThreadProc(LPVOID lpParameter) { // 為當前線程分配 TLS 數據 int* tlsData = (int*)malloc(sizeof(int)); *tlsData = (int)(size_t)lpParameter; // 將線程傳遞的參數放入 TLS 數據 TlsSetValue(g_TlsIndex, tlsData); // 使用 TLS 數據 printf("Thread %d: TLS Data = %d\n", GetCurrentThreadId(), *tlsData); // 模擬工作 Sleep(1000); // 釋放 TLS 數據 free(tlsData); return 0;
} int main() { // 1. 分配 TLS 索引 g_TlsIndex = TlsAlloc(); if (g_TlsIndex == TLS_OUT_OF_INDEXES) { printf("Failed to allocate TLS Index\n"); return 1; } // 2. 創建線程 const int threadCount = 3; HANDLE threads[threadCount]; for (int i = 0; i < threadCount; ++i) { threads[i] = CreateThread(NULL, 0, ThreadProc, (LPVOID)(size_t)(i + 1), 0, NULL); } // 等待線程完成 WaitForMultipleObjects(threadCount, threads, TRUE, INFINITE); // 3. 清理 for (int i = 0; i < threadCount; ++i) { CloseHandle(threads[i]); } // 釋放 TLS 索引 TlsFree(g_TlsIndex); return 0;
}
內部實現
-
每個線程都會分配一個線程環境塊(Thread Environment Block,TEB),TEB 結構中包含一個用于存儲 TLS 數據的區域:
-
動態 TLS 的數據存儲在 TEB 的
TlsSlots
數組中,每個槽對應一個TlsAlloc()
返回的索引。 -
靜態 TLS 的數據在程序加載時由系統內存分配,并初始化到對應的線程。
特性 動態分配 TLS (Win32 API) 靜態分配 TLS (__declspec(thread)) 易用性 手動管理,需自己分配和釋放 TLS 數據 自動完成線程初始化和銷毀,使用方便 性能 每個訪問可能需要一層索引查找,性能稍低 編譯時分配,直接訪問內存,性能高 生命周期 動態分配,程序員負責管理 生命周期由操作系統控制 靈活性 可以動態創建任意數量的 TLS 只能事先聲明固定的 TLS 變量
回調機制
-
TLS 提供線程生命周期管理的機制:TLS 回調函數。這些回調函數會在以下情況下被調用:
-
線程附加(Thread Attach):當線程啟動時,初始化 TLS 數據。
-
線程分離(Thread Detach):當線程結束時,清理 TLS 數據。
#include <windows.h> #ifdef _WIN64
#pragma comment (linker, "/INCLUDE:_tls_used")
#else
#pragma comment (linker, "/INCLUDE:__tls_used")
#endif //#pragma comment (linker, "/INCLUDE:pTLS_CALLBACKs") _declspec(thread) DWORD dw = 0x12345678;
_declspec(thread) DWORD dw1 = 0xCCCCCCCC;void NTAPI TLS_CALLBACK(PVOID DllHandle, DWORD Reason, PVOID Reserved) //TLS callback function
{}void NTAPI TLS_CALLBACK1(PVOID DllHandle, DWORD Reason, PVOID Reserved) //TLS callback function
{}#ifdef _WIN64
#pragma const_seg(".CRT$XLB")
EXTERN_C const
#else
#pragma data_seg(".CRT$XLX")
#endif PIMAGE_TLS_CALLBACK pTLS_CALLBACKs[] = { TLS_CALLBACK, TLS_CALLBACK1,0 };#ifdef _WIN64
#pragma const_seg()
#else
#pragma data_seg()
#endif int main(void)
{return 0;
}
TLS Directory 的結構
TLS 數據由 IMAGE_TLS_DIRECTORY 結構描述,其定義如下
typedef struct _IMAGE_TLS_DIRECTORY { ULONGLONG StartAddressOfRawData; // TLS 數據起始地址(RVA) ULONGLONG EndAddressOfRawData; // TLS 數據結束地址(RVA) ULONGLONG AddressOfIndex; // TLS 索引表地址 ULONGLONG AddressOfCallBacks; // TLS 回調數組地址 DWORD SizeOfZeroFill; // 初始化為零的大小 DWORD Characteristics; // 保留字段(通常為 0)
} IMAGE_TLS_DIRECTORY64, *PIMAGE_TLS_DIRECTORY64;
-
StartAddressOfRawData:
-
指的是 TLS 初始化數據段的起始地址。
-
這部分數據會復制到每個線程的 TLS 段中,作為初始化狀態。
-
-
EndAddressOfRawData:
-
TLS 初始化數據段的結束地址。
-
-
AddressOfIndex:
-
指向一個 TLS 索引(通常位于線程環境塊 TEB 中),用于標識當前線程的 TLS 段。
-
-
AddressOfCallBacks:
-
指向一個回調函數指針數組。在線程創建或退出時,這些回調函數會依次被調用。
-
-
SizeOfZeroFill:
-
表示未初始化數據的大小,如果存在,則這部分數據會在 TLS 數據段初始化時清零。
-
-
Characteristics:
-
通常保留字段,值通常為 0。
-