线程局部存储(TLS)

每个线程是没有自己单独的空间的,它们的所有的参数,变量都会放在该进程的空间里。为了实现局部变量人们引入了tls
用途:动态TLS和静态TLS这两项技术在创建DLL的时候更加有用,这是因为DLL通常并不知道它们被链接到的应用程序的结构是什么样的。
 动态TLS

系统中每个进程都有一组正在使用标志(in-use flags),每个标志可以被设为FREE或INUSE,表示该TLS元素是否正在被使用。

进程中的线程是通过使用一个数组来保存与线程相关联的数据的,这个数组由TLS_MINIMUM_AVAILABLE个元素组成,在WINNT.H文件中该值被定义为64个。也就是说当线程创建时,系统给每一个线程分配了一个数组,这个数组共有TLS_MINIMUM_AVAILABLE个元素,并且将这个数组的各个元素初始化为0,之后系统把这个数组与新创建的线程关联起来。每一个线程中都有它自己的数组,数组中的每一个元素都能保存一个32位的值。在使用这个数组前首先要判定,数组中哪个元素可以使用,这将使用函数TlsAlloc来判断。函数TlsAlloc判断数组中一个元素可用后,就把这个元素分配给调用的线程,并保留给调用线程。要为数组中的某个元素赋值可以使用函数TlsSetValue,要得到某个元素的值可以使用TlsGetValue。
这里写图片描述
一般通过调用一组4个API函数来使用动态TLS:TlsAlloc、TlsSetValue、TlsGetValue和TlsFree。
调用TlsAlloc函数时会都多该进程标志位里面标志为FREE的一项,找到好把该标志位置为INUSE后得到该标志位的数组索引。

当系统创建一个线程的时候,会分配TLS_MINIMUM_AVAILABLE个PVOID值,将它们都初始化为0,并与线程关联起来。每个线程都有自己的PVOID数组,数组中的每个PVOID可以保存任意值。在能够将信息保存到线程的PVOID数组中之前,我们必须知道数组中的哪个索引可供使用—这就是调用TlsAlloc的目的。TlsAlloc为我们预定了一个索引,如果为2,即TlsAlloc返回值为2,那么无论是进程中当前正在运行的线程,还是今后可能会创建的线程,都不能再使用该索引2了

2)为了把一个值放到线程的PVOID数组中,应该调用TlsSetValue函数:
BOOL WINAPI TlsSetValue(
__in DWORD dwTlsIndex, //索引值,表示在数组中的具体位置
__in_opt LPVOID lpTlsValue //要设置的值
);

当一个线程调用TlsSetValue函数成功时,它会修改自己的PVOID数组,但它无法修改另一个线程的TLS值。在调用TlsSetValue时,我们应该总是传入前面在调用TlsAlloc时返回的索引。因为Windows为了效率牺牲了对输入值的错误检测。

3)为了从线程的数组中取回一个值,应该调用函数TlsGetValue:
LPVOID WINAPI TlsGetValue(
__in DWORD dwTlsIndex //索引值
);

这个函数会返回在索引为dwTlsIndex的TLS元素中保存的值。TlsGetValue只会查看属于调用线程的数组。

4)当不再需要一个已经预定的TLS元素时,应该调用TlsFree函数:

BOOL WINAPI TlsFree(
__in DWORD dwTlsIndex //索引值
);

这个函数告诉系统已经预定的这个TLS元素现在不需要了,函数会将进程内的位标志数组中对应的INUSE标志重新设回FREE。此外,函数还会将所有线程中该元素的内容设为0.

#include<windows.h>
#include<stdio.h>
#define THREADCOUNT 4
DWORD dwTlsIndex;
VOID ErrorExit(LPSTR);
VOID CommonFunc(VOID)
{
    LPVOID lpvData;
    lpvData = TlsGetValue(dwTlsIndex);
    if ((lpvData == 0) && (GetLastError() != ERROR_SUCCESS))
        ErrorExit("TlsGetValue error");
    printf("common:thread %d:lpvData=%lx\n", GetCurrentThreadId(), lpvData);
    Sleep(5000);
}
DWORD WINAPI ThreadFunc(VOID)
{
    LPVOID lpvData;
    //为该线程初始化啊tls索引
    lpvData = (LPVOID)LocalAlloc(LPTR, 256);//返回一个指向新地址指针的句柄,PTR为分配固定内存初始化内容为zero
    if (!TlsSetValue(dwTlsIndex, lpvData))
        ErrorExit("TlsSetVal error");
    printf("thread %d:lpvoData=%lx", GetCurrentThreadId(), lpvData);
    CommonFunc();
    lpvData = TlsGetValue(dwTlsIndex);
    if (lpvData != 0)
        LocalFree((HLOCAL)lpvData);
    return 0;
}

int main(VOID)
{
    DWORD IDThread;
    HANDLE hThread[THREADCOUNT];
    int i;
    if ((dwTlsIndex) = TlsAlloc() == TLS_OUT_OF_INDEXES)//在标志位数组里找到一个标志为free的值,返回它的数组索引,并为这个索引申请空间
        ErrorExit("TlsAlock faild");
    //创建多个线程
    for (i = 0; i < THREADCOUNT; ++i)
    {
        hThread[i] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ThreadFunc
            , NULL, 0, &IDThread);
        if (hThread[i] == NULL)
            ErrorExit("CreatThread error\n");
    }
    for (i = 0; i < THREADCOUNT; ++i)
        WaitForSingleObject(hThread[i], INFINITE);
}
/*
CreatThread函数
第一个参数:定义新线程安全属性
二:分配以字节数表示的线程堆栈的大小,默认值是0;
三:指向一个线程函数地址。每个线程都有自己的线程函数,线程函数是线程具体的执行代码
四:传递给线程函数的参数
五:表示创建线程的运行状态,其中CREATE_SUSPEND表示挂起当前创建的线程,而0表示立即执行当前创建的进程;
六:返回新创建的线程的ID编号
返回值:则返回新线程的句柄,调用WaitForSingleObject函数等待所创建线程的运行结束。函数的格式如下:
    WaitForSingleObject()函数:
        hHandle:指定对象或时间的句柄;
        dwMilliseconds:等待时间,以毫秒为单位,当超过等待时间时,此函数返回。如果参数设置为0,则该函数立即返回;如果设置成INFINITE,则该函数直到有信号才返回。
*/
void ErrorExit(LPSTR lpszMessage)
{
    fprintf(stderr, "%s\n", lpszMessage);
    ExitProcess(0);
}
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值