線程本地變量允許程序中的每個(gè)線程都有一個(gè)獨(dú)立的實(shí)例拷貝??梢允褂?code>thread_local關(guān)鍵字來(lái)對(duì)這樣的變量進(jìn)行聲明。命名空間內(nèi)的變量,靜態(tài)成員變量,以及本地變量都可以聲明成線程本地變量,為了在線程運(yùn)行前對(duì)這些數(shù)據(jù)進(jìn)行存儲(chǔ)操作:
thread_local int x; // 命名空間內(nèi)的線程本地變量
class X
{
static thread_local std::string s; // 線程本地的靜態(tài)成員變量
};
static thread_local std::string X::s; // 這里需要添加X(jué)::s
void foo()
{
thread_local std::vector<int> v; // 一般線程本地變量
}
由命名空間或靜態(tài)數(shù)據(jù)成員構(gòu)成的線程本地變量,需要在線程單元對(duì)其進(jìn)行使用前進(jìn)行構(gòu)建。有些實(shí)現(xiàn)中,會(huì)將對(duì)線程本地變量的初始化過(guò)程,放在線程中去做;還有一些可能會(huì)在其他時(shí)間點(diǎn)做初始化,在一些有依賴的組合中,根據(jù)具體情況來(lái)進(jìn)行決定。將沒(méi)有構(gòu)造好的線程本地變量傳遞給線程單元使用,不能保證它們會(huì)在線程中進(jìn)行構(gòu)造。這樣就可以動(dòng)態(tài)加載帶有線程本地變量的模塊——變量首先需要在一個(gè)給定的線程中進(jìn)行構(gòu)造,之后其他線程就可以通過(guò)動(dòng)態(tài)加載模塊對(duì)線程本地變量進(jìn)行引用。
函數(shù)中聲明的線程本地變量,需要使用一個(gè)給定線程進(jìn)行初始化(通過(guò)第一波控制流將這些聲明傳遞給指定線程)。如果函數(shù)沒(méi)有被指定線程調(diào)用,那么這個(gè)函數(shù)中聲明的線程本地變量就不會(huì)構(gòu)造。本地靜態(tài)變量也是同樣的情況,除非其單獨(dú)的應(yīng)用于每一個(gè)線程。
靜態(tài)變量與線程本地變量會(huì)共享一些屬性——它們可以做進(jìn)一步的初始化(比如,動(dòng)態(tài)初始化);如果在構(gòu)造線程本地變量時(shí)拋出異常,srd::terminate()
就會(huì)將程序終止。
析構(gòu)函數(shù)會(huì)在構(gòu)造線程本地變量的那個(gè)線程返回時(shí)調(diào)用,析構(gòu)順序是構(gòu)造的逆順序。當(dāng)初始化順序沒(méi)有指定時(shí),確定析構(gòu)函數(shù)和這些變量是否有相互依存關(guān)系就尤為重要了。當(dāng)線程本地變量的析構(gòu)函數(shù)拋出異常時(shí),std::terminate()
會(huì)被調(diào)用,將程序終止。
當(dāng)線程調(diào)用std::exit()
或從main()函數(shù)返回(等價(jià)于調(diào)用std::exit()
作為main()的“返回值”)時(shí),線程本地變量也會(huì)為了這個(gè)線程進(jìn)行銷毀。應(yīng)用退出時(shí)還有線程在運(yùn)行,對(duì)于這些線程來(lái)說(shuō),線程本地變量的析構(gòu)函數(shù)就沒(méi)有被調(diào)用。
雖然,線程本地變量在不同線程上有不同的地址,不過(guò)還是可以獲取指向這些變量的一般指針。指針會(huì)在線程中,通過(guò)獲取地址的方式,引用相應(yīng)的對(duì)象。當(dāng)引用被銷毀的對(duì)象時(shí),會(huì)出現(xiàn)未定義行為,所以在向其他線程傳遞線程本地變量指針時(shí),就需要保證指向?qū)ο笏诘木€程結(jié)束后,不能對(duì)相應(yīng)的指針進(jìn)行解引用。