技术标签: 线程私有数据 线程本地存储 聊聊glibc TSD TLS 变量模型
从本篇开始进入另一个话题:线程本地存储(Thread Local Storage),在介绍这个概念前先说说变量和多线程的相关知识。
在单线程模型下,变量定义有两个维度,那就是在何处定义,以及它的修饰属性(static, extern,auto,register等)。extern属性表示声明一个变量 ,与定义无关,在此不作讨论;而register是将变量优化成寄存器里面,不作讨论。与变量定义相关的修饰属性就只有auto和static了。这两个维度可以得到变量的类型,以及它们的行为。评价一个变量的行为,通过常是从可见性和
生命周期两方面评定的,下面将变量的“何处定义”和“修饰属性”,得出“变量类型”以及从“可见性”和“生命周期”列出一个完整的表格,如下:
何处定义 | 修饰属性 | 变量类型 | 生命周期 | 可见性 |
---|---|---|---|---|
在函数外 | auto | 全局变量 | 整个程序 | 全局可见 |
在函数外 | static | 全局静态变量 | 整个程序 | 同一.c文件可见 |
在函数内 | auto | 局部变量 | 函数开始执行到结束这段时间 | 函数内可见 |
在函数内 | static | 静态局部变量 | 整个程序 | 函数内可见 |
对于静态局部变量会有这样的疑问,它只能在函数内使用,为什么它的生命周期是全局的?其实它与局部变量是不一样的,局部变量在函数开始执行时,才创建出来的,在函数执行结束时,它就消失了,不存在了。而静态局部变量,在函数没有执行时,它就已经在存在了,这就是为什么说它是整个程序的生命周期。当函数执行结束之后,它仍然存在,下次再执行该函数时,它是基于之前执行后的结果,开始执行的。所以它的行为与全局变量是一样的,不同之外是只能在函数内才能对它进行访问。
在单线程编程下面,这一切都协调得很好。因为整个进程只有一个执行单元,何任时刻只有一个执行代码(或者函数)在访问同一变量。
然而在多线程模式下,这一切都发生了变化。在多线程模式下面,同时出现多个执行单元,也即同一个函数会同时在多个执行单元中运行
提起多线程,几乎都想起应用加锁进行保护数据,但这不是充分必要的。
局量变量(生命周期在函数内的)是不受多线程影响,因为每个线程栈是独立的,多个线程同时执行该函数,访问局变部变量都是每线程一份的,不会产生数据竞争(race)。
但对于生命周期为整个程序的全局变量、全局静态变量和静态局部变量就不是那么一回事了。多线程下出现多个执行单元同时访问该变量,会出现数据竞争。在这种情况下,必须使用加锁进行数据保护,才能保证程序逻辑的正确性。
提起加锁,直觉反应就是将部分代码又回到串行执行的时代,如果加锁很频繁,那整个进程能并发执行的代码比例并不高,使用多线程带来的加速比并不高,最重要的是无法根据处理器个数来动态伸缩。
在多程线程序设计中,很多时候对数据进行划分,不同的数据区域交给不同的线程处理,可以避免多线程中竞争访问数据。但是有一个问题得考虑,那就是:各个独立线程在执行函数时(通常是不同线程执行相同的函数,只是他们访问的数据不同),这些函数的中间输出结果能否已是线程独立的呢?如果能完全独立,则多线程之间完全不需要互斥,伸缩性最优。
这个问题有两个解决办法,首先想到的是这些函数的输入和输出全部用参数传递。但它对编程要求很高, 有时这样的设计往往并不是最优的。很多时候函数的中间输出结果需要使用全局变量来保存(当然我们不推荐全局变量满天飞,但在一个模块内的静态全局变量还是常有的),这样就需要加锁保护,效率低下。
在这个场景下,每个线程都希望自己看到的全局变量是自己的,它也不想看到别人的,也不想别人对它有修改,它也不想修改别人的,全局变量也是个形式而已,最终目的是它可以独立拥有一份。
那么要解决多线程下的高效编程问题,必须对原来的变量模型稍作修改,支持一个额外的属性,那就是变量是多执行单元(线程)共享还是独立拥有一份。
何处定义 | 修饰属性 | 多执行单元共享or独享 | 变量类型 | 生命周期 | 可见性 |
---|---|---|---|---|---|
在函数外 | auto | 共享 | 全局变量 | 整个程序 | 全局可见 |
在函数外 | auto | 独享 | 全局线程变量 | 整个程序 | 全局可见 |
在函数外 | static | 共享 | 全局静态变量 | 整个程序 | 同一.c文件可见 |
在函数外 | static | 独享 | 全局静态线程变量 | 整个程序 | 同一.c文件可见 |
在函数内 | auto | 独享 | 局部变量 | 只在函数开始执行到结束这段时间 | 函数内 |
在函数内 | static | 共享 | 静态局部变量 | 整个程序 | 函数内可见 |
在函数内 | static | 独享 | 静态局部线程变量 | 整个程序 | 函数内可见 |
注意,局部变量本来就是线程函数内独享的,所以它没有共享这个属性值,整个程序生命周期的变量都有共享和独享这两个属性值。
写到这里,TLS的定义就不言而喻。TLS全称为Thread Local Storage,即线程本地存储。在单线程模式下,所有整个程序生命周期的变量都是只有一份,那是因为只是一个执行单元;而在多线程模式下,有些变量需要支持每个线程独享一份的功能。这种每线程独享的变量放到每个线程专有的存储区域,所以称为线程本地存储(Thread Local Storage)或者线程私有数据(Thread Specific Data)。
Linux下支持两种方式定义和使用TLS变量,具体如下表:
定义方式 | 支持层次 | 访问方式 |
---|---|---|
__thread关键字 | 语言层面 | 与全局变量完全一样 |
pthread_key_create函数 | 运行库层面 | pthread_get_specific和pthread_set_specific对线程变量进行读写 |
应用语言支持的__thread关键字是最简单的,只须在定义变量时增加一个__thread关键字,后续对该变量的访问方式完全保持不变,所以这个是语言级别上的支持,属于隐式支持。_thread关键字是gcc对C语言的扩展,不是C语言标准定义的,当然Windows下的Visual Stdio也使用另一个关键字做扩展。
下面使用简单的例子说明__thread关键字的用法,以及实际的效果。
__thread int var = 0; // var定义为线程变量,每个数线拥有一份
void* worker(void* arg);
int main(){
pthread_t pid1,pid2;
pthread_create(&pid1,NULL,worker,(void *)0);
pthread_create(&pid2,NULL,worker,(void *)1);
pthread_join(pid1,NULL);
pthread_join(pid2,NULL);
return 0;
}
void* worker(void* arg){
int idx = (int)arg;
int i;
for (i = 0; i < 100; ++i) {
printf("thread:%d ++var = %d\n", idx, ++var); // 每个线程只访问自己独享的那份
}
}
使用pthread_key_create/pthread_get_specific/pthread_set_specific函数创建和使用的线程变量,在后面的会作相应的介绍,我们暂时不作讨论。
后面文章中,我们使用线程变量这一术语表示每线程独享一份的变量,而使用TLS述语来表示对这一类变量存储属性的统称。
文章浏览阅读650次。上面是效果原因 每次都是编译在大文件里面我希望只有BIN文件在一个干净的地方写一个.BAT在任何地方都是可以执行的 最后挂在KEIL里面内容 mv.batFOR /F %%I IN ('DIR /B /S "D:\TSBrowserDownloads\DA145xx_SDK_for_handover\DA145xx_SDK\old\projects\Izar\src\Node_Dialog_DA14531_SHENNONG\Keil_5\out_DA14531\Ob..._nrfutil' 不是内部或外部命令,也不是可运行的程序
文章浏览阅读427次,点赞24次,收藏17次。在使用计算机过程中,有时会遇到“计算机缺失msvcp80.dll文件”的错误提示,这直接影响了部分应用程序的正常运行。msvcp80.dll是Microsoft Visual C++ 2005 redistributable runtime library(即VC++ 2005运行时库)的一部分,对于基于VC++ 2005编译的应用程序至关重要。本文将深入探究此问题产生的原因,并提出切实可行的解决方案。_msvcp80.dll
文章浏览阅读54次。2016/03/04 12:00第一二章:JS的简史以及基本语法1.P112.variable3.P13 等于4.P135.P14 转义字符6.关联数组不是一个好习惯7.P18 对象8.P31firefox和chrome的兼容性;+1900,IE好着呢;第三章:强大的DOM编程1.DOM:Document O..._dom编程艺术第3版下载
文章浏览阅读9.6k次,点赞7次,收藏35次。摘要:2015年3月,我作为项目经理参与了某公司与XX市交通运输局的道路交通智能监控抓拍系统的建设工作。我作为项目经理,主要进行了需求分析、系统设计、项目管理等工作。我十分重视项目的进度管理,运行丰富的项目管理经验,结合进度管理理论,对项目的各阶段进行了进度管理:规划进度管理、定义活动、估算活动顺序、估算活动资源、估算活动持续时间、制定进度计划、控制项目进度等过程全面展开对沟通的管控。依照项目管理..._信息系统集成项目管理工程师高级论文
文章浏览阅读317次。 https://www.cnblogs.com/heimianshusheng/p/5663913.html
文章浏览阅读1.7k次。 近几年UI设计行业一直都比较火,不少其他行业的设计师都转行UI设计。这时候可能就会有小伙伴问未来职业规范怎么做才能脱颖而出呢?今天胡老师和大家来探讨一下。 现在的UI设计的市场需求和刚兴起那会截然不同,那时只要会设计图标简单的界面就可以找到一份很不错工作。而且薪资也比较可观。因此UI设计瞬间爆火,还有很多设计同行也分分转战UI设计。这个职位的特点,一定是指数型的,好的人会越来越好,一般的人面对的门槛则会提高。其实任何行业和职业都是这样的,只不过在互联网的设计师、工程师(以及其他职位)中,尤其明显。_ui设计未来工作期望
文章浏览阅读1.7k次。将nfs作为mysql的数据目录输出后,在另一台主机上启动mysql进程时,会出现如下这样的错误,究其原因,其实还是nfs自身设计的缺陷。 初始化就是使用特定的用户,去特定的目录去更新mysql,虽然说添加mysql用户之后,所有的对数据的修改权限都是以mysql用户执行的,而且nfs的数据目录也都设计成了mysql,常理是没有问题的。但是,执行mysql_ins_influxdb数据库 nfs存储初始化失败
文章浏览阅读2.5k次。今天有客户问了我一下关于ORC事务表与Hyperbase表的区别问题,我回答的不是特别好,所以这里总结一下他们两个的区别,以便能掌握得更加深入些。ORC事务表:轻量级索引,支持CRUD操作,但是不建议大规模的单条增删改查,因为TDH(TDH是星环自研的一套大数据平台,类似于CDH,但是进行了很多的优化)是大数据数仓系统,是需要使用批量进行增删改查,索引单条操作的性能会降低;事务表需要进..._星环 hyperbase、orc、text表区别
文章浏览阅读261次。Mybatis环境:JDK1.8Mysql5.7maven 3.6.1IDEA回顾JDBCMysqlJava基础MavenJunit1. 简介1.1 什么是MybatisMyBatis 是一款优秀的持久层框架它支持自定义 SQL、存储过程以及高级映射MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java _"mybatis the content of element type \"choose\" must match \"(when*,otherwise?)"
文章浏览阅读700次。1 算法介绍1.1 萤火虫算法算法基本思想描述如下:在群体中,每个萤火虫个体被随机分布在目标函数定义的空间中,初始阶段,所有的萤火虫都具有相同的荧光素值和动态决策半径。其中,每个萤火虫个体根据来自动态决策半径内所有邻居萤火虫信号的强弱来决定其移动的方向。萤火虫的动态决策半径会随着在它范围内萤火虫个体的数目而变化,每个萤火虫的荧光素也会随着决策半径内萤火虫个体的数目而改变。萤火虫群优化算法是无记忆的,无需目标函数的全局信息和梯度信息,具有计算速度快,调节参数少,易于实现等特点。萤火虫进化过程中,每次._mape1=mean(abs(error./output_test));
文章浏览阅读588次。这种情况一般是因为在generatorConfig.xml文件中,连接数据库时缺少serverTimeZone导致的,在数据库连接上加上serverTimeZone=UTC即可解决。generatorConfig.xml代码如下:<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator.._mybatisplus报错the server time zone value ' й
文章浏览阅读5.3k次,点赞13次,收藏109次。相机标定是获得目标工件精准坐标信息的基础。首先,必须进行相机内参标定,构建一个模型消除图像畸变;其次,需要对相机和机器人的映射关系进行手眼标定,构建一个模型将图像坐标系上的点映射到世界坐标系。主要分为背景知识、相机内外参模型推导、编程代码实现三个部分。_机器视觉标定