線程局部存儲區(Thread Local Storage, TLS):將數據與一個正在執行的特定函數關聯起來。
線程局部存儲是將現有函數變為線程安全的有用技巧。
當一個函數中訪問並修改全局或靜態變量,那么這個函數就是不可重入的。若使之變為可重入的函數,可以使用線程同步,也可以使用線程局部存儲。線程局部存儲為每一個訪問此變量的線程提供一個此變量獨立的副本,線程可以修改此變量,而不會影響到其他線程。
注:通過以上描述可以看出,線程局部存儲不是用來共享變量的。
C標准庫中的strtok就是一個典型的不可重入函數。當程序第一次調用它時,該函數會將傳入的字符串地址保存在它自己的靜態變量中;當再次調用strtok並傳入NULL時,該函數會自動引用保存下來的字符串地址。
想一想,如果一個線程正在使用strtok的時候,另外一個線程也調用此函數,那就會產生很大的問題,第一個線程傳入的字符串被替換掉了!為了解決這種問題,C/C++運行庫使用了TLS。
Windows中應用TLS的一個簡單示例:計算線程的運行時間
1 //
2
#include <windows.h>
3 #include <process.h>
4
5 #include <ctime>
6 #include <cstdlib>
7 #include <vector>
8 #include <iostream>
9
10 using namespace std;
11
12 unsigned int __stdcall threadProc( void *arg)
13 {
14 DWORD tlsIndex = reinterpret_cast<DWORD>(arg);
15 clock_t begin = clock();
16 TlsSetValue(tlsIndex, PVOID(begin) ); // 利用TlsSetValue 設置 值
17
18 printf( " \nbegin thread: %d, clock_t: %d ", GetCurrentThreadId() , begin);
19
20 Sleep( 3000);
21
22 clock_t end = clock();
23 clock_t diff = end - reinterpret_cast<clock_t>(TlsGetValue(tlsIndex) );
24 double sec = 1.0 * diff / CLOCKS_PER_SEC; // 利用TlsGetValue取得值
25
26 printf( " \nend thread: %d, clock: %d, live time %f ", GetCurrentThreadId(), end, sec );
27
28 return 0;
29 }
30
31 int main( int argc, char *argv[])
32 {
33 // 分配Tls索引
34 DWORD tlsIndex = TlsAlloc();
35
36 vector<HANDLE> threads;
37 for ( int i = 0; i < 50; ++i){
38 HANDLE h = (HANDLE)_beginthreadex(NULL, 0, threadProc, ( void*)tlsIndex, 0, NULL);
39 threads.push_back(h);
40 }
41
42 for (size_t i = 0; i < threads.size(); ++i){
43 WaitForSingleObject(threads[i], INFINITE);
44 CloseHandle(threads[i]);
45 }
46 }
47 //
3 #include <process.h>
4
5 #include <ctime>
6 #include <cstdlib>
7 #include <vector>
8 #include <iostream>
9
10 using namespace std;
11
12 unsigned int __stdcall threadProc( void *arg)
13 {
14 DWORD tlsIndex = reinterpret_cast<DWORD>(arg);
15 clock_t begin = clock();
16 TlsSetValue(tlsIndex, PVOID(begin) ); // 利用TlsSetValue 設置 值
17
18 printf( " \nbegin thread: %d, clock_t: %d ", GetCurrentThreadId() , begin);
19
20 Sleep( 3000);
21
22 clock_t end = clock();
23 clock_t diff = end - reinterpret_cast<clock_t>(TlsGetValue(tlsIndex) );
24 double sec = 1.0 * diff / CLOCKS_PER_SEC; // 利用TlsGetValue取得值
25
26 printf( " \nend thread: %d, clock: %d, live time %f ", GetCurrentThreadId(), end, sec );
27
28 return 0;
29 }
30
31 int main( int argc, char *argv[])
32 {
33 // 分配Tls索引
34 DWORD tlsIndex = TlsAlloc();
35
36 vector<HANDLE> threads;
37 for ( int i = 0; i < 50; ++i){
38 HANDLE h = (HANDLE)_beginthreadex(NULL, 0, threadProc, ( void*)tlsIndex, 0, NULL);
39 threads.push_back(h);
40 }
41
42 for (size_t i = 0; i < threads.size(); ++i){
43 WaitForSingleObject(threads[i], INFINITE);
44 CloseHandle(threads[i]);
45 }
46 }
47 //