Thread Local Storage



Instance(單件)機制原本是讓代碼執行時只有一個實例,但有的時候又希望每個線程各自能有自己的"單件"相互不影響,處理類似的需求最先想到的就是全局表,然後按線程id或是管理線程的key索引到對應的單件上,取全局表的時候需要加鎖。 雖然這樣也能實現目的,但是代碼看上去很不自然。最近發現還是有更自然的方法能實現這一點,就是 TLS 線程本地存儲(Thread Local Storage)

用編譯器實現這點比較簡單,如果是使用動態創建的方式來實現的單件 則需要申請一個key 來綁定使用這個TLS 單件

---------------------------------------------------------------------------------------------------------------------------

Compiler :

---------------------------------------------------------------------------------------------------------------------------

 _declspec (thread)  InstanceClassA g_ia; 

// 這句告訴給編譯器此變量爲線程內部使用,每個線程都會copy一份給本地用

---------------------------------------------------------------------------------------------------------------------------


windows 和 linux 下用到的api 稍有不同,但用法差不多,主要是通過申請key 綁定 內存對象 ,釋放key 釋放內存的 方式進行


WIN API :

---------------------------------------------------------------------------------------------------------------------------

static DWORD InstanceClassA::dwTlsIndex;    

// 定義一些DWORD線程全局變量或函數靜態變量,準備作爲各個線程訪問自己的TLS數組的索引變量

InstanceClassA::dwTlsIndex= TLSAlloc();

 // 當前線程實際上訪問的是這個TLS數組索引變量的線程內的拷貝版本。也就說,不同線程雖然看起來用的是同名的TLS數組索引變量,但實際上各個線程得到的可能是不同DWORD值。其意義在於,每個使用TLS的線程獲得了一個DWORD類型的線程局部靜態變量作爲TLS數組的索引變量。C/C++原本沒有直接定義線程局部靜態變量的機制,所以在如此大費周折

TlsSetValue(InstanceClassA::dwTlsIndex, new InstanceClassA); 

// 動態申請單件內存,然後把指向這塊內存區域的指針放入TLS數組相應的槽中,綁定上TlsIndex

InstanceClassA* gp_ia = TlsGetValue(InstanceClassA::dwTlsIndex); 

// 用 TlsIndex 取得單件

delete gp_ia ;  

// 先釋放單件內存 

TlsFree(InstanceClassA::dwTlsIndex);

// 然後 釋放TLS索引變量  ,如果dwTlsIndex先刪除了則很難釋放其綁定上的InstanceClassA單件了,在這點上 linux 提供了一個更爲保險的方法

----------------------------------------------------------------------------------------------------------------------------

LINUX API :

-----------------------------------------------------------------------------------------------------------------------------

static pthread_key_t InstanceClassA::dwTlsIndex;   

// 這步驟同win 一樣

static void InstanceClassA::destroy(void *p) { delete (InstanceClassA*)p; }   

// 爲索引提供一個釋放綁定對象的方法

pthread_key_create(&InstanceClassA::dwTlsIndex, &InstanceClassA::destroy); 

// 創建出索引並且提供釋放方法給索引將會綁定的對象

pthread_setspecific(InstanceClassA::dwTlsIndex, new InstanceClassA); 

// 綁定單件到索引 

InstanceClassA* gp_ia = pthread_getspecific(InstanceClassA::dwTlsIndex); 

// 用 索引 取得單件

pthread_key_delete(InstanceClassA::dwTlsIndex);  

// 釋放掉索引的同時 ,也會調用 InstanceClassA::destroy 釋放掉InstanceClassA


-----------------------------------------------------------------------------------------------------------------------------

static InstanceClassA*GetInstance()

{

InstanceClassA* pm = (InstanceClassA*)pthread_getspecific(InstanceClassA::dwTlsIndex);
// or TlsGetValue(InstanceClassA::dwTlsIndex)

if(!pm) pthread_setspecific(InstanceClassA::dwTlsIndex,
pm = new InstanceClassA());  // or
TlsSetValue(InstanceClassA::dwTlsIndex, new InstanceClassA)

return pm;

}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章