当前位置: 首页 > news >正文

聊聊Thread Local Storage

聊聊ThreadLocal

  • 为什么需要Thread Local Storage
  • Thread Local Storage的实现
    • PThread库实现
    • 操作系统实现
    • GCC __thread关键字实现
    • C++11 thread_local实现
    • JAVA ThreadLocal实现

Thread Local Storage 线程局部存储,简称TLS。

为什么需要Thread Local Storage

变量分为全局变量局部变量

  • 全局变量:在全局范围内有效,其生命周期跟程序进程的生命周期一致,即在程序启动时初始化,在程序结束时被销毁。
  • 局部变量:只在某段代码块内有效,其生命周期是代码块被执行期间,即在进入该段代码块时初始化,离开该段代码块时销毁(对自带垃圾回收的语言,这个销毁会有点滞后)。

全局变量可以用于在多线程间传递数据,非常方便,但需要考虑并发访问冲突问题,一般都需要同步代码块/加锁访问。局部变量只能在代码块内访问,在多线程间互不干扰,无须考虑并发访问冲突问题。

在日常工作中,我们可能会碰到以下场景:希望每个线程拥有自己的变量副本(Thread Local Storage),这样该变量(也称为ThreadLocal变量)在线程间互不干扰,从而避免并发访问冲突问题
比如随机数生成场景中,生成的伪随机数生成,即当随机数种子固定后,那么生成的随机数序列都是固定的。为了保证随机数的随机性,就可以将随机数种子声明为ThreadLocal,这样在不同的线程中,这些随机数种子不同,从而不同线程生成的随机数序列也不同。
比如linux系统中的errno变量,该变量是全局变量,很早之前都是单线程模型,errno的用法没问题,但后来支持多线程了,errno变量值就受到多线程干扰了,为了保证多线程的errno能正确返回,只能通过Thread Local Storage的方式,无法通过加锁的方式保证。

Thread Local Storage的实现

Thread Local Storage的本质就是每个线程都有该变量副本。

PThread库实现

在C语言中,可以使用Pthread库来实现线程局部存储。
Pthread库提供了一种称为线程特定数据(Thread-Specific Data, TSD)的机制,允许每个线程关联一组键值对。每个线程可以通过键来访问和修改其关联的值,而不会影响其他线程中的相同键的值。
在内部,Pthread库通常会为每个线程维护一个线程局部存储的数据结构(如哈希表),用于存储键值对。每个线程在访问或修改其局部存储的数据时,都会通过这个数据结构进行操作。

在这里插入图片描述

为了使用TSD的特性,Pthread库提供了以下方法

//创建键,即获取一个keys数组的索引
int pthread_key_create(pthread_key_t *key, void (*destructor)(void*));
//设置键关联的数据
int pthread_setspecific(pthread_key_t key, const void *value);
//获取键关联的数据
void *pthread_getspecific(pthread_key_t key);
//释放键,即重置键关联keys数组中对应的值,以便其他变量使用
int pthread_key_delete(pthread_key_t key);
  • 键(Key)的创建和管理
    使用pthread_key_create函数可以创建一个键,该键可以被多个线程共享。创建键时,可以指定一个析构函数,当线程结束时,该函数会被调用来释放与键关联的数据。
  • 数据的设置和获取
    使用pthread_setspecific函数可以将数据与特定的键和线程关联起来。使用pthread_getspecific函数可以获取与特定键和线程关联的数据。
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>// 定义一个键
pthread_key_t key;// 线程函数
void* thread_func(void* arg) {int* data = (int*)malloc(sizeof(int));*data = *(int*)arg;pthread_setspecific(key, data);// 获取并打印线程局部存储的数据int* retrieved_data = (int*)pthread_getspecific(key);printf("Thread %ld: data = %d\n", pthread_self(), *retrieved_data);return NULL;
}int main() {pthread_t thread1, thread2;int data1 = 10, data2 = 20;// 创建键pthread_key_create(&key, free);// 创建线程pthread_create(&thread1, NULL, thread_func, &data1);pthread_create(&thread2, NULL, thread_func, &data2);// 等待线程结束pthread_join(thread1, NULL);pthread_join(thread2, NULL);// 销毁键pthread_key_delete(key);return 0;
}

操作系统实现

操作系统在实现Thread Local Storage机制上主要考虑以下方面:

  • TLS数据结构的分配
    操作系统为每个线程分配一个独立的TLS区域,用于存储该线程的所有TLS变量。在编译期间可以确定TLS变量个数,所以这个区域通常是一个固定大小的内存块。
  • TLS变量的存储
    每个线程可能会访问多个TLS变量,操作系统会为每个TLS变量分配一个唯一的偏移量,这个偏移量表示该变量在TLS区域中的位置。线程可以通过这个偏移量访问自己的TLS变量。
  • TLS变量的访问
    当线程需要访问一个TLS变量时,操作系统会提供一组特殊的指令或函数,用于从当前线程的TLS区域中获取该变量的值。这些指令或函数通常会使用线程ID和TLS变量的偏移量来计算变量的实际地址。
  • TLS变量的初始化
    操作系统会在每个线程开始执行时自动初始化TLS变量。对于全局范围的TLS变量,操作系统会在进程启动时为其分配内存并进行初始化。对于函数范围内的TLS变量,操作系统会在函数调用时为其分配内存并进行初始化。
  • TLS变量的销毁
    当线程结束时,操作系统会自动回收其TLS区域,并释放相应资源。

GCC __thread关键字实现

GCC通过使用操作系统提供的线程局部存储(Thread Local Storage,TLS)机制来实现**__thread关键字**。__thread关键字用于声明线程局部变量。这些变量在每个线程中都有独立的实例,互不干扰。当线程结束时,这些变量的生命周期也随之结束。
以下是GCC实现__thread关键字的一些关键步骤:

  • 生成TLS变量
    当你在代码中使用__thread关键字声明一个变量时,GCC会为该变量生成一个TLS符号。这个符号在程序的整个生命周期内都存在,但在不同的线程中具有不同的值。
    例如:
    __thread int counter = 0;
    编译后,GCC会生成一个类似于_ZL7counter的TLS符号。
  • 分配TLS空间
    在程序启动时,操作系统会为每个线程分配一块TLS空间。这块空间的大小取决于程序中声明的TLS变量的数量。GCC会在程序初始化时计算所需的TLS空间大小,并将其传递给操作系统。Linux默认最大只支持1024个TLS变量。
  • 访问TLS变量
    当线程访问一个__thread变量时,GCC会生成一段特殊的代码,用于从当前线程的TLS空间中获取该变量的值。这段代码通常是一个内存访问指令,其地址由线程ID和TLS偏移量计算得出。
    例如,访问上面的counter变量时,GCC可能会生成类似以下的代码:
    movl $_ZL7counter@TLSGD(%rip), %eax
    这段代码将当前线程的TLS空间中counter变量的值加载到寄存器%eax中。
  • 初始化TLS变量
    GCC会在每个线程开始执行时自动初始化__thread变量。对于全局范围的__thread变量,GCC会在程序启动时为其分配内存并进行初始化。对于函数范围内的静态变量,GCC会在首次调用时为其分配内存并进行初始化。
  • 销毁TLS变量
    当线程结束时,操作系统会自动回收其TLS空间,并释放相应资源。

__thread的使用限制

  • 只能修饰POD类型(类似整型指针的标量,不带自定义的构造、拷贝、赋值、析构的类型,二进制内容可以任意复制memset,memcpy,且内容可以复原)。
  • 不能修饰class类型,因为无法自动调用构造和析构函数。
  • 可用于修饰全局变量,函数内的静态变量,不能修饰函数的局部变量或class的普通成员变量。
  • __thread变量值只能初始化为编译器常量
  • __thread限定符(specifier)可以单独使用,也可带有extern或static限定符,但不能带有其它存储类型的限定符。
  • __thread可用于全局的静态文件作用域,静态函数作用域或一个类中的静态数据成员。不能用于块作用域,自动或非静态数据成员。

C++11 thread_local实现

c++11提供的thread_local实现跟GCC __thread实现类似,都是借助操作系统的TLS机制实现的。但是c++11提供的thread_local可跨平台使用,也可修饰非POD类型的变量。

#include <iostream>
#include <thread>// 声明一个线程局部变量
thread_local int thread_local_var = 0;void thread_function(int thread_id) {// 更新线程局部变量的值thread_local_var = thread_id;std::cout << "Thread " << thread_id << ": thread_local_var = " << thread_local_var << std::endl;
}int main() {// 创建两个线程std::thread t1(thread_function, 1);std::thread t2(thread_function, 2);// 等待线程结束t1.join();t2.join();return 0;
}

JAVA ThreadLocal实现

Java采用的实现方案跟上面类似,也是每个线程一个数组,专门用来存储变量副本。
ThreadLocal实现
由图可知,每个线程使用ThreadLocalMap存储ThreadLocal对应的具体值,在读写ThreadLocal变量对应的值时,最终都是到table中读写。由于不同线程的table不一样,虽然ThreadLocal变量一致,但是对应的值不一样,这样就实现了不同线程有不同的数据副本。

相关文章:

聊聊Thread Local Storage

聊聊ThreadLocal 为什么需要Thread Local StorageThread Local Storage的实现PThread库实现操作系统实现GCC __thread关键字实现C11 thread_local实现JAVA ThreadLocal实现 Thread Local Storage 线程局部存储&#xff0c;简称TLS。 为什么需要Thread Local Storage 变量分为全…...

WEB攻防-JS项目Node.js框架安全识别审计验证绕过

知识点&#xff1a; 1、原生JS&开发框架-安全条件 2、常见安全问题-前端验证&未授权 详细点&#xff1a; 1、什么是JS渗透测试&#xff1f; 在JavaScript中也存在变量和函数&#xff0c;当存在可控变量及函数调用即可参数漏洞 2、流行的Js框架有哪些&#xff1f; …...

STM32——SPI

1.SPI简介 SPI&#xff0c;是英语Serial Peripheral Interface的缩写&#xff0c;顾名思义就是串行外围设备接口。SPI&#xff0c;是一种高速的&#xff0c;全双工&#xff0c;同步的通信总线&#xff0c;并且在芯片的管脚上只占用四根线&#xff0c;节约了芯片的管脚&#xf…...

【云安全】云上资产发现与信息收集

一、云基础设施组件 1、定义 在云计算基础架构中&#xff0c;基础设施组件通常包括&#xff1a;计算、存储、网络和安全等方面的资源。例如&#xff0c;计算资源可以是虚拟机、容器或无服务器计算引擎&#xff1b;存储资源可以是对象存储或块存储&#xff1b;网络资源可以是虚拟…...

flask搭建微服务器并训练CNN水果识别模型应用于网页

一. 搭建flask环境 概念 flask:一个轻量级 Web 应用框架&#xff0c;被设计为简单、灵活&#xff0c;能够快速启动一个 Web 项目。CNN:深度学习模型&#xff0c;用于处理具有网格状拓扑结构的数据&#xff0c;如图像&#xff08;2D网格&#xff09;和视频&#xff08;3D网格&a…...

数据篇| 关于Selenium反爬杂谈

友情提示:本章节只做相关技术讨论, 爬虫触犯法律责任与作者无关。 LLM虽然如火如荼进行着, 但是没有数据支撑, 都是纸上谈兵, 人工智能的三辆马车:算法-数据-算力,缺一不可。之前写过关于LLM微调文章《微调入门篇:大模型微调的理论学习》、《微调实操一: 增量预训练(Pretrai…...

MySQL高阶1890-2020年最后一次登录

目录 题目 准备数据 分析数据 题目 编写解决方案以获取在 2020 年登录过的所有用户的本年度 最后一次 登录时间。结果集 不 包含 2020 年没有登录过的用户。 返回的结果集可以按 任意顺序 排列。 准备数据 Create table If Not Exists Logins (user_id int, time_stamp …...

update-alternatives官方手册

下述手册超链接都是英文&#xff0c;内容差不多&#xff0c;看一个就行 Debian系统的Ubuntu系统的《The Linux Programming Interface》图书上的...

cesium.js 入门到精通(5-2)

在cesium 的配置中 有一些参数 可以配置地图的显示 显示出 水的动态显示 山的效果 相当于一些动画显示的效果 var viewer new Cesium.Viewer("cesiumContainer", {infoBox: false,terrainProvider: await Cesium.createWorldTerrainAsync({requestWaterMask: tru…...

LINUX的PHY抽象层——PAL

英文原文参考&#xff1a; https://www.kernel.org/doc/html/latest/networking/phy.html 中文翻译参考&#xff1a;有关PHY抽象层的总结 https://blog.csdn.net/eydwyz/article/details/124753313 目录 1 前言2 PHY接口模式3 尽量使用PHY端的延时而不是MAC或PCB4 其他方式实现…...

优先级队列(堆)

目录 优先级队列 堆的概念 堆的创建 堆的向下调整 堆的插入 完整代码 优先级队列 队列是一种先进先出的数据结构&#xff0c;有些时候操作的数据可能带有优先级&#xff0c;出队列时就需要优先级高的数据先出队列。 在这种情况下&#xff0c;数据结构应该提供两个最基本…...

帧率和丢帧分析理论

一、丢帧问题概述 应用丢帧通常指的是在应用程序的界面绘制过程中&#xff0c;由于某些原因导致界面绘制的帧率下降&#xff0c;从而造成界面卡顿、动画不流畅等问题。以60Hz刷新率为例子&#xff0c;想要达到每秒60帧&#xff08;即60fps&#xff09;的流畅体验&#xff0c;每…...

solidwork找不到曲面

如果找不到曲面 则右键找到选项卡&#xff0c;选择曲面...

mac安装JetBtains全家桶新版本时报错:Cannot start the IDE

mac安装JetBtains全家桶新版本时报错&#xff1a;Cannot start the IDE 前言报错信息解决方法 前言 作者使用的是Mac电脑&#xff0c;最近想要更新JetBrains相关工具的软件版本&#xff0c;但是在安装时突然报错&#xff0c;导致安装失败&#xff0c;现在将报错信息以及解决方…...

MVCC机制解析:提升数据库并发性能的关键

MVCC机制解析&#xff1a;提升数据库并发性能的关键 MVCC&#xff08;Multi-Version Concurrency Control&#xff09; 多版本并发控制 。 MVCC只在事务隔离级别为读已提交(Read Committed)和可重复读(Repeated Read)下生效。 MVCC是做什么用的 MVCC是为了处理 可重复读 和…...

如何使用Postman搞定带有token认证的接口实战!

现在许多项目都使用jwt来实现用户登录和数据权限&#xff0c;校验过用户的用户名和密码后&#xff0c;会向用户响应一段经过加密的token&#xff0c;在这段token中可能储存了数据权限等&#xff0c;在后期的访问中&#xff0c;需要携带这段token&#xff0c;后台解析这段token才…...

Linux Vim编辑器常用命令

目录 一、命令模式快捷键 二、编辑/输入模式快捷键 三、编辑模式切换到命令模式 四、搜索命令 注&#xff1a;本章内容全部基于Centos7进行操作&#xff0c;查阅本章节内容前请确保您当前所在的Linux系统版本&#xff0c;且具有足够的权限执行操作。 一、命令模式快捷键 二…...

【Android】浅析MVC与MVP

【Android】浅析MVC与MVP 什么是架构&#xff1f; 架构&#xff08;Architecture&#xff09;在软件开发中指的是软件系统的整体设计和结构&#xff0c;它描述了系统的高层组织方式&#xff0c;包括系统中各个组件之间的关系、依赖、交互方式&#xff0c;以及这些组件如何协同…...

spark 面试题

spark 面试题 1、spark 任务如何解决第三方依赖 比如机器学习的包&#xff0c;需要在本地安装&#xff1f;--py-files 添加 py、zip、egg 文件不需要在各个节点安装 2、spark 数据倾斜怎么解决 spark 中数据倾斜指的是 shuffle 过程中出现的数据倾斜&#xff0c;主要是由于…...

青柠视频云——如何开启HTTPS服务?

前言 由于青柠视频云的语音对讲会使用到HTTPS服务&#xff0c;这里我们说一下如何申请证书以及如何在实战中部署并且配置使用。 一、证书申请 1、进入控制台 我们拿阿里云的免费个人证书为例&#xff0c;首先登录阿里云&#xff0c;在控制台找到数字证书管理服务&#xff0c;进…...

2016年国赛高教杯数学建模A题系泊系统的设计解题全过程文档及程序

2016年国赛高教杯数学建模 A题 系泊系统的设计 近浅海观测网的传输节点由浮标系统、系泊系统和水声通讯系统组成&#xff08;如图1所示&#xff09;。某型传输节点的浮标系统可简化为底面直径2m、高2m的圆柱体&#xff0c;浮标的质量为1000kg。系泊系统由钢管、钢桶、重物球、…...

vue-使用refs取值,打印出来是个数组??

背景&#xff1a; 经常使用$refs去获取组件实例&#xff0c;一般都是拿到实例对象&#xff0c;这次去取值的时候发现&#xff0c;拿到的竟然是个数组。 原因&#xff1a; 这是vue的特性,自动把v-for里面的ref展开成数组的形式&#xff0c;哪怕你的ref名字是唯一的&#xff01…...

微服务_入门1

文章目录 一、 认识微服务二、 微服务演变2.1、 单体架构2.2、 分布式架构2.3、 微服务2.4、 微服务方案对比 三、 注册中心3.1、 Eureka3.2、 Nacos3.2.1、服务分级存储模型3.2.2、权重配置3.2.3、环境隔离 一、 认识微服务 二、 微服务演变 随着互联网行业的发展&#xff0c;…...

【学习资料】袋中共36个球,红白黑格12个,问能一次抽到3个红4个白5个黑的概率是多少?

1、公式计算 1.1 题目1 袋中共 36 36 36个球&#xff0c; 红 \fcolorbox{red}{#FADADE}{\color{red}{红}} 红​ 白 \fcolorbox{white}{#808080}{\color{white}{白}} 白​ 黑 \fcolorbox{#808080}{#0D0D0D}{\color{#808080}{黑}} 黑​各 12 12 12个&#xff0c;问能一次抽到 3…...

@PathVariable,@RequestParam,@RequestBody注解,springboot与前端请求之间的数据类型转换

前端数据与springboot java数据类型转换 springboot&mybatis中数组和字符串数据类型的转换-CSDN博客中曾经提到&#xff0c;在Spring Boot中&#xff0c;通过URL传参、payload中的key-value形式或json形式&#xff0c;将前端数据以字符串格式发送到后端&#xff0c;后端We…...

在Python中优雅地打开和操作RDS

在Python中优雅地打开和操作RDS 随着数据存储需求的不断增长&#xff0c;关系数据库服务&#xff08;Relational Database Service, RDS&#xff09;成为了许多企业首选的数据存储方式。那么&#xff0c;在Python中如何轻松地与RDS进行交互呢&#xff1f;以下是一份详尽的指南…...

.whl文件下载及pip安装

以安装torch_sparse库为例 一、找到自己需要的版本&#xff0c;点击下载。 去GitHub的pyg-team主页中找到pytorch-geometric包。网址如下&#xff1a; pyg-team/pytorch_geometric​github.com/pyg-team/pytorch_geometric 然后点击如图中Additional Libraries位置的here&am…...

望繁信科技受邀出席ACS2023,为汽车行业数智化护航添翼

2023年5月25-26日&#xff0c;ACS2023第七届中国汽车数字科技峰会在上海成功举行。此次峰会汇聚了众多汽车领域的顶级专家、产业链代表及企业高管&#xff0c;共同探讨当今汽车产业的转型与未来发展趋势。 作为唯一受邀的流程挖掘厂商代表&#xff0c;望繁信科技携最新行业优势…...

基于 C语言的 Modbus RTU CRC 校验程序

一、CRC校验原理 Modbus RTU是一种常用于工业设备通信的协议&#xff0c;它基于串行通信&#xff0c;如RS-232或RS-485。在Modbus RTU中&#xff0c;CRC&#xff08;循环冗余校验&#xff09;是一种常用的错误检测机制&#xff0c;用于确保数据在传输过程中的完整性和准确性。 …...

基于微信小程序的剧本杀游玩一体化平台

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、SSM项目源码 系统展示 基于微信小程序JavaSpringBootVueMySQL的剧…...