线程局部存储(TLS)
线程局部存储区(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 //