多线程的时候最好不要使用全局和静态变量,因为如果一个线程改变了全局变量的值,另一个线程在不知情的情况下有可能引用这个变量导致不可预料的错误
我们先举个例子
假设两个线程各自从0-9999计数
如果我们这样写
main.cpp
#include <Windows.h>
#include <stdio.h>
DWORD WINAPI WorkThread(LPVOID param);
DWORD g_dwNumber = 0;//旧版有问题
int main()
{
g_TlsIndex = TlsAlloc();
HANDLE hThread[2];
for (int i = 0; i < 2; i++)
{
hThread[i]= CreateThread(NULL, 0, WorkThread, NULL, 0, NULL);
}
WaitForMultipleObjects(2, hThread, true, INFINITE);
printf("%d", g_TlsIndex);
system("pause");
return 0;
}
DWORD WINAPI WorkThread(LPVOID param)
{
for (int i = 0; i < 10000; i++)
{
printf("PID=%d g_dwNumber=%d\n", GetCurrentThreadId(), g_dwNumber++);
}
return 0;
}
运行结果
最后的计数结果达到了19999,很显然是错误的
这个时候就是动态tls派上用场的时候了,动态tls其实就是为每个线程创建一个与其关联的内存块,这个内存块可以当数组使用
TlsAlloc可以分配一个没有用过的索引给你,让你在里面写入东西
这里需要注意的一点是只要申请了一个索引,所有的线程都能使用这个索引,但是索引所在位置的内容是与线程相关的
打个比方,一个市有市长,副市长,地税局长,国税局长,招商局张,教育局长等等,市就相当于线程,而省相当于进程,每个市的市长和副市长都是不一样,所以说,每个线程每个tls数组相关索引里面的内容都是跟线程相关的
使用动态tls需要4个函数
TlsAlloc() 分配可用的tls索引
TlsFree(DWORD dwTlsIndex) 释放索引,这样下次可以继续使用
TlsSetValue(DWORD dwTlsIndex,LPVOID p) 为tls数组索引位置设置数据
TlsGetValue(DWORD dwTlsIndex) 获取tls数组索引位置的数据
下面我们举个例子吧
#include <Windows.h>
#include <stdio.h>
DWORD WINAPI WorkThreadNew(LPVOID param);
DWORD g_TlsIndex = 0;//tls添加
int main()
{
g_TlsIndex = TlsAlloc();//使用之前先分配一个索引
HANDLE hThread[2];
for (int i = 0; i < 2; i++)
{
hThread[i]= CreateThread(NULL, 0, WorkThreadNew, NULL, 0, NULL);
}
WaitForMultipleObjects(2, hThread, true, INFINITE);
printf("%d", g_TlsIndex);
TlsFree(g_TlsIndex);
system("pause");
return 0;
}
DWORD WINAPI WorkThreadNew(LPVOID param)
{
TlsSetValue(g_TlsIndex, 0);
for (int i = 0; i < 10000; i++)
{
int n = (int)TlsGetValue(g_TlsIndex);
Sleep(1);
printf("PID=%d g_dwNumber=%d\n", GetCurrentThreadId(), n);
TlsSetValue(g_TlsIndex, (LPVOID)(++n));
}
return 0;
}
大功告成