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;
}