1. 程式人生 > >執行緒區域性儲存

執行緒區域性儲存

執行緒區域性儲存
英文為Thread Local Storage [1] ,縮寫為TLS。為什麼要有TLS?原因在於,全域性變數與函式內定義的靜態變數,是各個執行緒都可以訪問的共享變數。

在一個執行緒修改的記憶體內容,對所有執行緒都生效。這是一個優點也是一個缺點。說它是優點,執行緒的資料交換變得非常快捷。說它是缺點,一個執行緒死掉了,其它執行緒也性命不保; 多個執行緒訪問共享資料,需要昂貴的同步開銷,也容易造成同步相關的BUG。
如果需要在一個執行緒內部的各個函式呼叫都能訪問、但其它執行緒不能訪問的變數(被稱為static memory local to a thread 執行緒區域性靜態變數),就需要新的機制來實現。這就是TLS。
執行緒區域性儲存在不同的平臺有不同的實現,可移植性不太好。幸好要實現執行緒區域性儲存並不難,最簡單的辦法就是建立一個全域性表,通過當前執行緒ID去查詢相應的資料,因為各個執行緒的ID不同,查到的資料自然也不同了。但Windows系統採用了每個執行緒建執行緒專享的索引表,表的條目為執行緒區域性儲存的地址。線上程執行的任何程式碼處,都可以查詢本執行緒的這個索引表獲得要訪問的執行緒區域性儲存的地址。
大多數平臺都提供了執行緒區域性儲存的方法,無需要我們自己去實現:
摘自百度

比起美國婦女來,WINDOWS就不那麼開放了,一個程序被建立的時候,系統就偷偷的為該程序下每個執行緒建立了一個用於儲存資料的"執行緒儲存陣列"(長度都相同),併為這個程序建立用於管理執行緒位陣列索引的一個"索引陣列"(每個程序只有一個),這些是系統完成的,在CreateProcess函式和 CreateThread函式中是沒有體現出來的,所以有點神祕感,以下是我的一些理解:

1、每個執行緒可以通過程序的索引陣列的索引訪問自己儲存資料的"執行緒儲存陣列",索引陣列成員值有FREE(標誌該索引對應下的“每個執行緒”儲存陣列成員空閒)和 INUSE(標誌該索引對應下下的每個執行緒儲存陣列成員已用)執行緒使用自己的儲存陣列前通過主執行緒呼叫TlsAlloc函式返回一個數組成員值為 FREE(說明執行緒儲存陣列該索引成員空閒可用)的索引;
2、系統使程序索引位陣列的長度和每個執行緒儲存資料的位陣列長度一樣,否則索引位陣列如果有100位,而每個執行緒儲存陣列有101位,索引陣列就沒法索引這101位的資料並使用它,長度一樣是管理上的需要;
3、程序的索引陣列成員FREE和INUSE的設定是:比如一個新建程序,程序的索引陣列a[n]成員值全部是FREE,而全部執行緒的執行緒儲存陣列成員都是空閒,出現某執行緒首次呼叫TlsAlloc函式時返回比如索引陣列成員第一個a[0]就是FREE的索引,TlsAlloc函式將a[0]值改為 INUSE,該執行緒使用TlsSet函式將自己的執行緒儲存陣列b[0]的成員使用,在a[0]未被TlsFree函式釋放前,所用執行緒的執行緒儲存陣列 b[0]都可以使用,不是一個執行緒用一次儲存陣列就必須要主執行緒呼叫一次TlsAlloc函式;
4、索引陣列可以通過TlsFree函式釋放索引(即改a[0]值為FREE)和b[0]空間,保證程序的索引陣列不會全部被佔用;
5、象很多核心物件一樣"執行緒儲存陣列"和"索引陣列"也是WINDOWS封閉保護的私處只一,不會讓你象自己定義個數組那樣直觀、直接、明白的使用,而是提供幾個TlsAlloc、TlsSetValue、TlsGetValue、TlsFree這樣的函式間接使用,所以感覺有點曖昧難懂;
6、動態使用TLS的步驟:
(1)主執行緒呼叫TlsAlloc函式分配標記執行緒儲存陣列未被使用成員的索引A,並將程序索引位陣列的該索引成員值由FREE改為INUSE;
(2)執行緒使用TlsAlloc函式分配的索引使用TlsSetValue(索引A,要設定的值)設定自己執行緒儲存陣列成員的值以便使用,或者用TlsGetValue函式讀取成員值使用;

(3)主執行緒呼叫TlsFree(索引A)釋放索引A和儲存陣列空間,已保證索引和儲存空間的迴圈使用;