windows程序设计——线程局部存储

线程局部存储
英文为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和存储数组空间,已保证索引和存储空间的循环使用;

原文:http://www.manongjc.com/article/37010.html

你可能感兴趣的:(window程序设计,c&c++)