[笔记]Windows核心编程《二十一》线程本地存储器TLS

简介: Windows核心编程《二十一》线程本地存储器TLS

文章目录



前言


引入用线程本地存储器TLS原因

由于运行期库是在多线程应用程序出现前的许多年前的设计的,因此运行期库中的大多数函数是用于单线程应用程序的。


   例如strtok,在多线程环境中,一个线程可以调用 strtok,然后,在它能够再次调用该函数之前,另一个线程也可以调用strtok。在这种情况下,第二个线程会在第一个线程不知道的情况下,让strtok用一个新地址来改写它的静态变量。第一个线程将来调用strtok时将使用第二个线程的字符串,这就会导致各种各样难以发现和排除的错误。

   这也叫不可重入函数,上次调用函数结果可能会影响这次调用结果.


为了解决这个问题,C / C + +运行期库使用了T L S。每个线程均被赋予它自己的字符串指针,供strtok函数使用。需要予以同样对待的其他C / C + +运行期库函数还有asctime和gmtime。


一、动态TLS


若要使用动态T L S,应用程序可以调用一组 4个函数。这些函数实际上是 D L L用得最多的函数。

windows用来管理TLS的内部数据结构:

图片.png

每个标志均可设置为 F R E E或者I N U S E,表示T L S时隙( s l o t )是否正在使用。


TlsAlloc函数

若要使用动态T L S,首先必须调用T l s A l l o c函数:

DWORD TlsAlloc( );

这个函数命令系统对进程中的位标志进行扫描,并找出一个 F R E E标志。然后系统将该标志从FREE改为INUSE,并且TlsAlloc返回位数组中的标志的索引。


TlsSetValue函数

要将一个值放入线程的数组,可以调用TlsSetValue函数:

BO0L TlsSetValue(DWORD dwTlsIndex,PVOID pvTlsValue);


TlsGetValue函数

PVOID TlsGetValue(DWORD dwTlsIndex) ;

该函数返回的值将与索引 dwTlsIndex处的 T L S时隙联系起来。与 TlsSetValue一样,TlsGetValue只查看属于调用线程的数组。


TlsFree函数

当在所有线程中不再需要保留T L S时隙的位置的时候,

BOOL TlsFree(DWORD dwTlsIndex) ;

由进程的位标志数组管理的I N U S E标志再次被设置为F R E E。


使用动态T L S

通常情况下,如果 DLL使用TLS,那么当它用DLL_PROCESS_ATTACH标志调用它的DllMain函数时,它也调用TlsAlloc。当它用DLL_PROCESS_DETACH调用DllMain函数时,它就调用TlsFree。对TlsSetValue和TlsGetValue的调用很可能是在调用DLL中包含的函数时进行的。


二、静态TLS


你想要将起始时间与应用程序创建的每个线程联系起来。只需要将起始时间变量,声明为下面的形式:


__declspec(thread)DWORD gt_dwStartTime = 0;

当编译器对程序进行编译时,它将所有的TLS变量放入它们自己的节,这个节的名字是.tls。链接程序将来自所有对象模块的所有 .tls节组合起来,形成结果的可执行文件或 D L L文件中的一个大的.tls节。


   缺点:


       编译器必须生成一些辅助代码来引用该静态 T L S变量,这将使你的应用程序变得比较大而且运行的速度比较慢。在 x86 CPU上,将为每次引用的静态 T L S变量生成3个辅助机器指令。

       系统允许包含静态 T L S变量的库在运行期进行显式加载,但是T L S数据没有进行相应的初始化。如果试图访问这些数据,就可能导致访问违规。


总结


1.动态TLS和静态TLS区别?

动态TLS:

  • 主要线程数据存放在内存中,加载dll时载入,卸载dll时释放。

静态TLS:

  • 主要线程数据存在dll文件的TLS节中,增加DLL大小。




相关文章
|
4天前
|
Java
【编程侦探社】追踪 Java 线程:一场关于生命周期的侦探故事!
【6月更文挑战第19天】在Java世界中,线程如同神秘角色,编程侦探揭示其生命周期:从新生(`new Thread()`)到就绪(`start()`),面临并发挑战如资源共享冲突。通过`synchronized`实现同步,处理阻塞状态(如等待锁`synchronized (lock) {...}`),最终至死亡,侦探深入理解并解决了多线程谜题,成为编程侦探社的传奇案例。
|
16天前
|
消息中间件 存储 缓存
【嵌入式软件工程师面经】Linux系统编程(线程进程)
【嵌入式软件工程师面经】Linux系统编程(线程进程)
27 1
|
23天前
|
Java 开发者 UED
掌握Java多线程编程:从基础到高级
【5月更文挑战第31天】本文深入探讨了Java多线程编程的核心概念,包括线程的创建、生命周期、同步机制以及高级并发工具。通过实际示例和代码片段,读者将学会如何有效地管理和协调线程,以编写高效且稳定的并发应用程序。
|
23天前
|
安全 Java 调度
Java语言多线程编程技术深度解析
Java语言多线程编程技术深度解析
291 1
|
3天前
|
存储 安全 算法
Java并发编程中的线程安全性与性能优化
在Java编程中,特别是涉及并发操作时,线程安全性及其与性能优化是至关重要的问题。本文将深入探讨Java中线程安全的概念及其实现方式,以及如何通过性能优化策略提升程序的并发执行效率。
8 1
|
5天前
|
Java 程序员
Java多线程编程是指在一个进程中创建并运行多个线程,每个线程执行不同的任务,并行地工作,以达到提高效率的目的
【6月更文挑战第18天】Java多线程提升效率,通过synchronized关键字、Lock接口和原子变量实现同步互斥。synchronized控制共享资源访问,基于对象内置锁。Lock接口提供更灵活的锁管理,需手动解锁。原子变量类(如AtomicInteger)支持无锁的原子操作,减少性能影响。
18 3
|
4天前
|
安全 Java 调度
Java并发编程:优化多线程应用的性能与安全性
在当今软件开发中,多线程编程已成为不可或缺的一部分,尤其在Java应用程序中更是如此。本文探讨了Java中多线程编程的关键挑战和解决方案,重点介绍了如何通过合理的并发控制和优化策略来提升应用程序的性能和安全性,以及避免常见的并发问题。
10 1
|
4天前
|
Java
【编程炼金术】Java 线程:从一粒沙到一个世界,生命周期的奇妙转化!
【6月更文挑战第19天】Java线程生命周期始于`Thread`类或`Runnable`接口,经历创建、新生、就绪、运行、阻塞到死亡五态。调用`start()`使线程进入就绪,随后可能获得CPU执行权变为运行态。当阻塞后,线程返回就绪,等待再次执行。理解并管理线程生命周期是优化多线程程序的关键。
|
6天前
|
数据采集 安全 算法
Java并发编程中的线程安全与性能优化
在Java编程中,多线程并发是提升程序性能的关键之一。本文将深入探讨Java中的线程安全性问题及其解决方案,并介绍如何通过性能优化技术提升多线程程序的效率。
13 3
|
7天前
|
编解码 Windows
FFmpeg开发笔记(二十九)Windows环境给FFmpeg集成libxvid
XviD是开源MPEG-4视频编码器,与DivX相似但后者非开源。早期MP4常使用XviD或DivX编码,现已被H.264取代。在Windows上集成FFmpeg的XviD编解码库libxvid,需访问<https://labs.xvid.com/source/>下载源码,解压后在MSYS环境中配置、编译和安装。之后重新配置FFmpeg,启用libxvid并编译安装。详细步骤包括configure命令、make和make install。成功后,通过`ffmpeg -version`检查是否启用libxvid。更多音视频开发技术可参考《FFmpeg开发实战:从零基础到短视频上线》。
36 0
FFmpeg开发笔记(二十九)Windows环境给FFmpeg集成libxvid