Windows核心编程学习笔记-------21章

21章线程局部存储区

有时将数据与一个对象的实例关联起来是有帮助的。比如窗口附加字节通过使用SetWindowWordSetWindowLong来将数据与一个指定的窗口关联起来。我们可用线程局部存储区(Thread Local Storage简称TLS)来将数据与一个正在执行的指定线程关联起来。例如,可将创建线程的时间与线程关联起来,当线程终止时,就可确定线程运行的时间长度。

C/C++运行库与TLS的联系:因其设计早于多线程,所以运行库中大多数函数是单线程应用程序设计的。如_tcstok_s会将传入的字符串地址保存在它自己的静态变量中,当thread1调用时,该静态变量保存一个值,当thread2再调用时,该静态变量的值被更改,若此时再调用thread1,则将不能得到想要的结果。为解决此问题,C/C++运行库使用了TLSC++运行库会为每个线程分配独立的字符串指针,专供_tcstok_s使用。

若程序高度依赖全局变量和静态变量,则TLS会成为救生符。所以开发时应尽量少用此类变量,更多使用自动变量(栈上的变量,其始终与特定线程关联)和通过参数传入的数据。

可在EXEDLL中使用动态TLS和静态TLS技术。一般来说,创建DLL时更有用,因DLL通常不知要被链接到的应用程序的结构。但编写EXE时,一般知道要创建多少线程,如何使用这些线程,这样就可用别的替代方案来为每个线程关联数据;设计的好的话,可用基于栈的方法(局部变量)来为每个线程关联数据。

21.1动态TLS

         原理:系统中每个进程都有一组正在使用标志,每个标志可被设为FREEINUSE,表示该TLS元素是否正在使用。Microsoft保证至少有TLS_NIMINUM_AVAILABLE个位标志可供使用(TLS_NIMINUM_AVAILABLEWinNT.h中被定义为64,系统会在需要时分配更多的TLS元素)。每个线程创建时,系统自动分配TLS_NIMINUM_AVAILABLEPVOID值,它们都初始化为0,并与线程关联起来。进程位标志数组元素个数与每个线程PVOID数组的元素个数相同。

         使用时,先让系统检索进程的位标志来获取第一个可用的FREE标志的索引,然后将此索引保存到线程的PVOID数组中,此索引不会再被其他线程所使用。

         用完后,通过TlsFree释放已预定的TLS的元素,同时会将进程内的位标识数组中对应的INUSE标志改回FREE,还会将所有线程中该元素的内容设为0

步骤:

         第一步先调用TlsAlloc,让系统对进程中的位标志进行检索并找到一个FREE标志。然后将FREE改为INUSE并让TlsAlloc返回该标志在位数组中的索引。一个DLL通常将此索引保存在一个全局变量中。因此值会在整个进程范围内使用,而非在线程范围内使用。

         第二步调用TlsSetValue将一个值放到调用线程的数组中。从线程的数组中取回值用TlsGetValue

         第三步当不再需要一个已经预定的TLS元素时,应调用TlsFree

使用动态TSL

         DLL要使用TLS,则会在DllMain处理DLL_PROCESS_ATTACH的时候调用TlsAlloc,在DllMain处理DLL_PROCESS_DETACH的时候调用TlsFree。而对TlsSetValueTlsGetValue的调用最可能发生在DLL所提供的其他函数中。

21.2静态TLS

         与动态TLS相似,静态TLS也将数据与线程关联起来。但使用时不必在代码中调用任何函数,更易使用。

         若想将应用程序创建的每个线程与该线程的启动时间关联起来,只需声明一个启动时间:__declspec(thread) DWORD gt_dwStartTime = 0; __declspec(thread)前缀告诉编译器应在可执行文件或DLL文件中,把对应的变量与特定线程相关。此修饰符修饰的变量必须被声明为全局变量或静态变量。局部变量不可用此修饰符,因其已与特定线程相关联。

 

DLL地狱问题:

比如现在建立好了一个DLL导出了CMyClass类,客户也能正常使用这个DLL,假设CMyClass对象的大小为30字节。如果我们需要修改DLL 中的CMyClass类,让它有相同的函数和成员变量,但是给增加了一个私有的成员变量int类型,现在CMyClass对象的大小就是34字节了。当直接把这 个新的DLL给客户使用替换掉原来30字节大小的DLL,客户应用程序期望的是30字节大小的对象,而现在却变成了一个34字节大小的对象,糟糕,客户程序出错 了。

类似的问题,如果不是导出CMyClass类,而在导出的函数中使用了CMyClass,改变对象的大小仍然会有问题的。这个时候修改这个问题的唯一办法就是替 换客户程序中的CMyClass的头文件,全部重新编译整个应用程序,让客户程序使用大小为34字节的对象。

这就是一个严重的问题,有的时候如果没有客户程序的源代码,那么我们就不能使用这个新的DLL了。

 

你可能感兴趣的:(Windows核心编程学习笔记-------21章)