執行緒區域儲存區
執行緒區域儲存區 (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。