執行緒區域儲存區 (TLS)
執行緒區域儲存區 (Thread Local Storage,TLS) 是一種方法,讓指定之多執行緒處理序中的每個執行緒用來配置位置,以儲存執行緒特定資料。 TLS API (TlsAlloc) 支援動態系結(運行時間)線程特定數據。 除了現有的 API 實作之外,Win32 和 Microsoft C++ 編譯程式現在還支援靜態系結的每個線程數據(載入時間)。
TLS 的編譯程序實作
C++11: 儲存 thread_local
類別規範是指定對象和類別成員線程本機記憶體的建議方式。 如需詳細資訊,請參閱記憶體類別(C++)。
MSVC 也提供Microsoft特定屬性 線程,做為擴充儲存類別修飾詞。 __declspec
使用 關鍵詞來宣告thread
變數。 例如,下列程式碼宣告整數執行緒區域變數,並使用值將它初始化:
__declspec( thread ) int tls_i = 1;
規則與限制
當您宣告靜態繫結的執行緒區域物件和變數時,必須遵守下列指導方針: 這些指導方針同時 適用於線程 和 thread_local:
屬性
thread
只能套用至類別和數據宣告和定義。 它不能用於函式宣告或定義。 例如,下列程式碼會產生編譯器錯誤:__declspec( thread )void func(); // This will generate an error.
thread
修飾詞只能在具有static
範圍的數據項上指定。 這包括全域數據物件(和static
extern
)、本機靜態物件,以及C++類別的靜態數據成員。 無法使用 屬性來thread
宣告自動資料物件。 下列程式碼會產生編譯器錯誤:void func1() { __declspec( thread )int tls_i; // This will generate an error. } int func2(__declspec( thread )int tls_i ) // This will generate an error. { return tls_i; }
線程區域物件的宣告和定義必須全部指定
thread
屬性。 例如,下列程式碼會產生錯誤:#define Thread __declspec( thread ) extern int tls_i; // This will generate an error, since the int __declspec( thread )tls_i; // declaration and definition differ.
屬性
thread
不能當做型別修飾詞使用。 例如,下列程式碼會產生編譯器錯誤:char __declspec( thread ) *ch; // Error
由於允許使用
thread
屬性C++物件的宣告,因此下列兩個範例在語意上是相等的:__declspec( thread ) class B { // Code } BObject; // OK--BObject is declared thread local. class B { // Code }; __declspec( thread ) B BObject; // OK--BObject is declared thread local.
線程區域對象的位址不會被視為常數,而且涉及這類位址的任何表達式都不會被視為常數表達式。 在標準 C 中,效果是不允許使用線程局部變數的位址做為物件或指標的初始化表達式。 例如,C 編譯器會將下列程式碼標示為錯誤:
__declspec( thread ) int tls_i; int *p = &tls_i; //This will generate an error in C.
此限制不適用於 C++。 因為 C++ 允許所有物件的動態初始化,所以您可使用採用執行緒區域變數位址的運算式來初始化物件。 就像建構線程區域對象一樣完成。 例如,先前所示的程式代碼在編譯為C++原始程序檔時不會產生錯誤。 只要取得地址的線程仍然存在,線程局部變數的位址才有效。
標準 C 允許使用包含本身參考的表達式來初始化物件或變數,但僅限於非靜態範圍的物件。 雖然C++通常允許具有參考本身之表達式的對象進行這類動態初始化,但線程區域對象不允許這種初始化。 例如:
__declspec( thread )int tls_i = tls_i; // Error in C and C++ int j = j; // OK in C++, error in C __declspec( thread )int tls_i = sizeof( tls_i ) // Legal in C and C++
sizeof
包含要初始化之對象的表達式並不代表本身的參考,而且會在 C 和 C++ 中啟用。C++不允許線程數據的動態初始化,因為線程本機儲存設施的未來可能增強功能。
在 Windows Vista 之前的 Windows 作業系統上,
__declspec( thread )
有一些限制。 如果 DLL 將任何資料或物件宣告為__declspec( thread )
,則會在動態載入時造成保護錯誤。 使用 LoadLibrary 載入 DLL 之後,每當程式代碼參考__declspec( thread )
數據時,就會造成系統失敗。 因為執行緒的全域變數空間是在執行階段進行配置,所以此空間的大小是以應用程式的需求,再加上以靜態方式連結之所有 DLL 的需求的計算為基礎。 當您使用LoadLibrary
時,無法擴充此空間,以允許以__declspec( thread )
宣告的線程局部變數。 如果您的 DLL 中可能會載入LoadLibrary
DLL,請使用 TLS API,例如 TlsAlloc 來設定 TLS。