[笔记]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大小。




相关文章
|
2月前
|
Java
如何在Java中进行多线程编程
Java多线程编程常用方式包括:继承Thread类、实现Runnable接口、Callable接口(可返回结果)及使用线程池。推荐线程池以提升性能,避免频繁创建线程。结合同步与通信机制,可有效管理并发任务。
172 6
|
5月前
|
Java API 微服务
为什么虚拟线程将改变Java并发编程?
为什么虚拟线程将改变Java并发编程?
311 83
|
2月前
|
Java 调度 数据库
Python threading模块:多线程编程的实战指南
本文深入讲解Python多线程编程,涵盖threading模块的核心用法:线程创建、生命周期、同步机制(锁、信号量、条件变量)、线程通信(队列)、守护线程与线程池应用。结合实战案例,如多线程下载器,帮助开发者提升程序并发性能,适用于I/O密集型任务处理。
295 0
|
3月前
|
算法 Java
Java多线程编程:实现线程间数据共享机制
以上就是Java中几种主要处理多线程序列化资源以及协调各自独立运行但需相互配合以完成任务threads 的技术手段与策略。正确应用上述技术将大大增强你程序稳定性与效率同时也降低bug出现率因此深刻理解每项技术背后理论至关重要.
274 16
|
7月前
|
机器学习/深度学习 消息中间件 存储
【高薪程序员必看】万字长文拆解Java并发编程!(9-2):并发工具-线程池
🌟 ​大家好,我是摘星!​ 🌟今天为大家带来的是并发编程中的强力并发工具-线程池,废话不多说让我们直接开始。
284 0
|
7月前
|
Web App开发 人工智能 JSON
Windows版来啦!Qwen3+MCPs,用AI自动发布小红书图文/视频笔记!
上一篇用 Qwen3+MCPs实现AI自动发小红书的最佳实践 有超多小伙伴关注,同时也排队在蹲Windows版本的教程。
1264 1
|
10月前
|
Linux
Linux编程: 在业务线程中注册和处理Linux信号
通过本文,您可以了解如何在业务线程中注册和处理Linux信号。正确处理信号可以提高程序的健壮性和稳定性。希望这些内容能帮助您更好地理解和应用Linux信号处理机制。
205 26
|
10月前
|
Linux
Linux编程: 在业务线程中注册和处理Linux信号
本文详细介绍了如何在Linux中通过在业务线程中注册和处理信号。我们讨论了信号的基本概念,并通过完整的代码示例展示了在业务线程中注册和处理信号的方法。通过正确地使用信号处理机制,可以提高程序的健壮性和响应能力。希望本文能帮助您更好地理解和应用Linux信号处理,提高开发效率和代码质量。
209 17
|
设计模式 Java 开发者
Java多线程编程的陷阱与解决方案####
本文深入探讨了Java多线程编程中常见的问题及其解决策略。通过分析竞态条件、死锁、活锁等典型场景,并结合代码示例和实用技巧,帮助开发者有效避免这些陷阱,提升并发程序的稳定性和性能。 ####
|
12月前
|
存储 安全 Java
Java多线程编程秘籍:各种方案一网打尽,不要错过!
Java 中实现多线程的方式主要有四种:继承 Thread 类、实现 Runnable 接口、实现 Callable 接口和使用线程池。每种方式各有优缺点,适用于不同的场景。继承 Thread 类最简单,实现 Runnable 接口更灵活,Callable 接口支持返回结果,线程池则便于管理和复用线程。实际应用中可根据需求选择合适的方式。此外,还介绍了多线程相关的常见面试问题及答案,涵盖线程概念、线程安全、线程池等知识点。
708 2

热门文章

最新文章