thread_local
變數
對於不是在 function 內宣告的
thread_local
變數,一定要以編譯時期常數來初始化,而且一定要用 ABSL_CONST_INIT 屬性確保這件事。 偏好使用thread_local
來定義執行緒內的區域變數。
定義
從 C++11 開始,變數可以在宣告時加上 thread_local
:
thread_local Foo foo = ...;
這樣的變數其實是對應到一堆物件的集合。 當不同的執行緒在存取變數的時候,其實會存取到不同的物件。 thread_local
變數其實在很多方面與 靜態儲存期變數 很接近。 舉例來說,它們可以被宣告在命名空間的作用域、函式內部、或是做為類別的靜態成員,但不能做為一般類別的成員。
thread_local
變數的實體的初始化很像靜態變數的作法,只差在它們必須在每個執行緒裡面分別初始化,而不是在程式啟動時一次初始化。 這代表著那些在函式內宣告的 thread_local
變數很安全,但是其他的 thread_local
變數則會像靜態變數一樣遭遇初始化順序的問題 (以及其他問題)。
thread_local
變數的實體在執行緒結束時被摧毀,所以他們沒有靜態變數的解構順序問題。
優點
- 執行緒內資料從本質上來說不會受到 data races 影響 (因為只有一個執行緒可以存取),這點讓
thread_local
對並行程式設計很有幫助。 thread_local
是唯一定義在標準內,用來建立執行緒內變數 (thread-local variables) 的方式。
缺點
- 存取
thread_local
的變數可能會觸發執行一些無法預期或是無法控制的程式碼。 thread_local
變數其實就是一個有效的全域變數,所以具備除了執行緒安全之外的所有全域變數的缺點。thread_local
變數帶來的記憶體消耗會隨著執行緒數量增加而增長,有可能會造成程式中很大的負擔。- 一般類別的成員不能是
thread_local
。 thread_local
可能不比某些編譯器的內建功能 (compiler intrinsics) 還要有效率。
我們的決定
定義在函式內的 thread_local
變數沒有安全疑慮,因此可以被無限制的使用。 注意你可以利用定義一個會回傳 thread_local
變數的函式或靜態方法,來使用函式作用域等級的 thread_local
以模擬類別或是命名空間作用域等級的 thread_local
:
Foo& MyThreadLocalFoo() {
thread_local Foo result = ComplicatedInitialization(); // 函式名稱:很複雜的初始化
return result;
}
類別或是命名空間作用域等級的 thread_local
變數必須使用編譯期就決定的常數來初始化 (也就是說,不能具有任何動態初始化的部分)。 為了要強迫這個限制,類別內或是名稱空間內的 thread_local
變數一定要標註 ABSL_CONST_INIT (或是 constexpr
,但不常用):
ABSL_CONST_INIT thread_local Foo foo = ...;
在定義執行緒內變數 (thread-local variables) 時應優先使用 thread_local
。