【并发编程十八】线程局部存储(TLS)

news2024/11/26 16:46:38

【并发编程十八】线程局部存储(TLS)

  • 一、定义
  • 二、线程局部存储的实现
    • 1、windows系统
    • 2、linux系统
    • 3、c++11
  • 三、windows系统
    • 1、线程局部存储是分块的(TLS_MINIMUM_AVAILABLE)
    • 2、获得索引
    • 3、通过索引:存储数据、取出数据
    • 4、释放索引和内存块
    • 5、编译器:提供定义线程局部变量的方法
    • 6、demo
  • 四、linux系统
    • 1、简介
    • 5、编译器:提供定义线程局部变量的方法
    • 6、demo
  • 五、C++11
    • 1、简介:thread_local
    • 2、能与static或extern结合
    • 3、demo
  • 六、线程局部存储重点

简介:
对于一个存在多个线程的进程来说,有时需要每个线程都自己操作自己的这份数据。这有点类似c++类的实例的属性,每个实例对象操作的都是自己的属性。我们把这样的数据成为线程局部存储(thread local storage,TLS)

一、定义

  • 线程局部存储是指对象内存在线程开始后分配,线程结束时回收,且每个线程有该对象自己的实例。
  • 简单的说,线程局部存储的对象都是独立于各个线程的。
  • 实际上,这不是一个新鲜的概念,虽然c++一直没有在语言层面支持它,但是很早之前操作系统就有办法执行线程局部存储了。(c++直到c++11才从语言层面实现了)

二、线程局部存储的实现

  • 由于线程本身是操作系统中的概念,因此线程局部存储这个功能是离不开操作系统支持的。
  • 而不同的操作系统对线程局部存储的实现也不相同,以至于使用的系统api也有区别,
  • 这里我们对windows和linux简单介绍下,对c++11提供的线程局部存储我们详细写下demo。

1、windows系统

2、linux系统

3、c++11

三、windows系统

1、线程局部存储是分块的(TLS_MINIMUM_AVAILABLE)

  • windows将线程局部存储区分成TLS_MINIMUM_AVAILABLE个块,每个块都通过1个索引值对外提供访问。
  • TLS_MINIMUM_AVAILABLE默认是64,在winnt.h文件中有如下定义:
# define TLS_MINIMUM_AVAILABLE 64

windows TLS结构示意图如下图所示
在这里插入图片描述

2、获得索引

  • 在windows中使用TlsAlloc函数获得一个线程局部存储块的索引
DWORD TlsAlloc();
  • 如果这个函数调用失败,则返回值是TLS_OUT_OFF_INDEXES(oxffffffff);如果这个函数调用成功,则会得到一个索引。

3、通过索引:存储数据、取出数据

  • 接下来就可以利用如下两个API函数分别在这个索引指向的内存块中存储数据,或者在这个索引指向的内存块中取出数据了
LPVOID TlsGetValue(DWORD dwTlsIndex)
BOOL TlsSetValue(DWORD dwTlsIndex, LPVOID lpTlsValue);

4、释放索引和内存块

当不再需要索引指向的内存块时,就可以使用如下函数来释放索引和内存块:

BOOL TlsFree(DWORD dwTlsIndex)

5、编译器:提供定义线程局部变量的方法

当然,在使用线程局部存储时除了可以使用API函数,还可以使用 Microsoft VC++ 编译器提供的如下方法定义一个线程局部变量:

__declspec(thread) int g_mydata =1

6、demo

我们可以看到,task1线程的改变g_mydata ,并没有对线程task2产生影响;

#include <iostream>
#include<Windows.h>
#include<thread>

using namespace std;

__declspec(thread) int g_mydata = 1;

void task1()
{
	while(true)
	{
		++g_mydata;
		Sleep(1000);
	}
}

void task2()
{
	for (int i = 0; i < 10; i++)
	{
		cout << "g_mydata=" << g_mydata << ",threadid=" << this_thread::get_id() << endl;//windows系统提供的获取线程id的系统函数是GetCurrentThreadId();
		Sleep(1000);
	}
}

int main()
{
	thread t1(task1);
	thread t2(task2);

	t1.join();
	t2.join();

	return 0;
}

输出

在这里插入图片描述

四、linux系统

1、简介

linux上的NTPL库提供了一套函数接口来实现线程局部存储

#include <pthread.h>

int pthread_key_create(pthread_key_t* key, void (*destructor)(void*));
int pthread_key_delete(pthread_key_t key);
int pthread_setspecific(pthread_key_t key, const void* value);
void* pthread_getspecific(pthread_key_t key);

pthread_key_delete:为线程局部存储创建一个新的key.
pthread_setspecific:设值
pthread_getspecific:获取值

参数destructor是一个自定义函数指针,其签名如下

void* destructor(void* value)
{
  // 多是为了释放value指针指向的资源
  }

线程终止时,如果key值管理的值不是NULL,那么NTPL会自动指向定义的destructor函数;如果无需解构他,则可以将destructor设置为NULL;

5、编译器:提供定义线程局部变量的方法

和Windows一样,linux的gcc也提供了一个关键字__thread用于定义线程局部变量;clang也是。

6、demo

五、C++11

1、简介:thread_local

虽然windows和Linux都有各自的方法声明线程局部存储变量,但是其使用范围和规则却存在一些区别,这种情况增加了c++的学习成本,也是c++标准委员会不愿意看到的。
于是,在c++11标准中整数添加了新的tread_local说明符来声明线程局部存储变量。

thread_local int g_mydata =1;

有了这个关键字,是哦那个线程局部存储的代码就可以同时在windows和 linux上运行了。

2、能与static或extern结合

thread_local说明符可以 用例声明线程生命周期的对象,它能与static或extern结合,分别指定内部或外部链接,不过额外的static并不影响对象的生命周期。换句话说,static并不影响线程局部存储的属性。

#include <iostream>
#include<Windows.h>
#include<thread>

using namespace std;

struct X {
	thread_local static int i;
};

thread_local X a;

int main()
{
	thread_local X b;

	return 0;
}

3、demo

把window下的简单改下关键字,就是c++11的demo

#include <iostream>
#include<Windows.h>
#include<thread>

using namespace std;

thread_local int g_mydata = 1;

void task1()
{
	while(true)
	{
		++g_mydata;
		Sleep(1000);
	}
}

void task2()
{
	for (int i = 0; i < 10; i++)
	{
		cout << "g_mydata=" << g_mydata << ",threadid=" << this_thread::get_id() << endl;//windows系统提供的获取线程id的系统函数是GetCurrentThreadId();
		Sleep(1000);
	}
}

int main()
{
	thread t1(task1);
	thread t2(task2);

	t1.join();
	t2.join();

	return 0;
}

输出

在这里插入图片描述

六、线程局部存储重点

  • 1、对于线程变量,每个线程都会有该变量的一个拷贝,互不影响,该局部变量一直存在,直到线程退出;
  • 2、系统的线程局部存储区域的内存空间并不大,所以尽量不要用这个空间存储大的数据块,如果不得不使用大的数据块,则可以将大的数据块存储在堆内存中,再将该堆内存的地址指针存储在线程局部存储区域。

参考:
1、《现代c++语言核心特性解析》谢丙堃 著;
2、《c++服务器开发精髓》 张远龙 著;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/364916.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

《计算机网络:自顶向下方法》实验1:协议分析软件的使用

实验1:协议分析软件的使用 实验步骤 启动主机上的web浏览器。 启动Ethereal(或WireShark)。你会看到如图2所示的窗口,只是窗口中没有任何分组列表。 开始分组捕获:选择“capture”下拉菜单中的“Start”命令,会出现如图所示的“Ethereal(或WireShark): Capture Options”…

(02)Cartographer源码无死角解析-(56) 2D后端优化→class MapById、MapById::lower_bound

讲解关于slam一系列文章汇总链接:史上最全slam从零开始&#xff0c;针对于本栏目讲解(02)Cartographer源码无死角解析-链接如下: (02)Cartographer源码无死角解析- (00)目录_最新无死角讲解&#xff1a;https://blog.csdn.net/weixin_43013761/article/details/127350885 文末…

Flink04: Flink核心API之DataSet

DataSet API主要可以分为3块来分析&#xff1a;DataSource、Transformation、Sink。 DataSource是程序的数据源输入。Transformation是具体的操作&#xff0c;它对一个或多个输入数据源进行计算处理&#xff0c;例如map、flatMap、filter等操作。DataSink是程序的输出&#xf…

攻击者查看邮件就被溯源到家?

本文通过分享实际攻防演练中真实案例&#xff0c;防守方在未暴露任何敏感信息的情况下&#xff0c;仅通过邮件往来最终溯源到攻击方相关真实信息。 作为攻击溯源技术的引子&#xff0c;供各位从业和爱好者交流学习。 场景描述 攻击者伪造邮件&#xff0c;称其申请防守方靶标系…

洛谷P5736 【深基7.例2】质数筛 C语言/C++

【深基7.例2】质数筛 题目描述 输入 nnn 个不大于 10510^5105 的正整数。要求全部储存在数组中&#xff0c;去除掉不是质数的数字&#xff0c;依次输出剩余的质数。 输入格式 第一行输入一个正整数 nnn&#xff0c;表示整数个数。 第二行输入 nnn 个正整数 aia_iai​&…

基于 esp-idf SDK ,如何在 .cpp 工程中加入.c 的文件调用?

把外部 .c 文件放到 .cpp 工程下的 main 文件夹然后在 .cpp 工程下声明 .c 文件下的 hello_main 函数同时在 cpp 工程的 CmakeLists.txt 文件下加上 .c 文件最后在 .cpp 工程下调用 hello_main 函数即可 可基于 esp-idf/examples/storage/nvs_rw_value_cxx 例程来测试 &#x…

mysql 8.0.32安装 windows server 超详细

官网下载mysql包&#xff0c;官网地址(中文版)&#xff1a; http://mysql.p2hp.com/cloud/index.html 我是下载的这个(第一个) 内容解压后是这样的&#xff0c;其实windows版本无需安装&#xff0c;只需要配置后启动即可 同时&#xff0c;建议下载下这个Visual Studio&#xf…

Nebula测试

LDBC benchmark 这是官方文档 https://ldbcouncil.org/ldbc_snb_docs/ldbc-snb-specification.pdf 主要有几点 Scale Factors 是生成数据的一个大小&#xff0c;For both workloads, the SF1 data set is 1 GiB, the SF100 is 100 GiB, and the SF10 000 data set is 10 000 G…

关于CSS的简单知识

CSS是什么首先&#xff0c;在之前的html仅仅是写了一个框架&#xff0c;页面并不工整&#xff0c;美观。而CSS正是解决了这一问题。HTML仅仅只是表示页面的结构和内容&#xff0c;而CSS描述的是页面的样式&#xff08;包括大小/位置/字体/颜色/背景等&#xff09;基本语言规范选…

[element plus] 对话框组件再封装使用 - vue

学习关键语句: 饿了么组件dialog组件使用 dialog组件二次封装 vue3中封住的组件使用update触发更新 vue3中封装组件使用v-model:属性值来传值 写在前面 这是我遇到的一个页面需求 , 其中一个对话框的内容是很常用的 , 所以我将它封装出来才写的一篇文章 现在给出如下需求: 封…

Git(分布式版本控制系统)

提到git了&#xff0c;我们先来说一下什么是git? 1、通俗一点&#xff0c;就是一个人工版本控制器 通过人工的复制行为来保存项目的不同阶段的内容&#xff0c;添加适当的一些描述文字加以区分 繁琐、容易出错 产生大量重复数据 2、什么是版本控制&#xff1f; 版本控制是指对…

JVM16命令行

2. JVM 监控及诊断工具-命令行篇 2.1. 概述 简单命令行工具 在我们刚接触 java 学习的时候&#xff0c;大家肯定最先了解的两个命令就是 javac&#xff0c;java&#xff0c;那么除此之外&#xff0c;还有没有其他的命令可以供我们使用呢&#xff1f; 我们进入到安装 jdk 的…

JAVA并发编程面试题合集

1.在Java中守护线程和本地线程的区别&#xff1f; Java中的线程分为两种&#xff1a;守护线程&#xff08;Daemon&#xff09;和用户线程&#xff08;User&#xff09;任何线程都可以设置为守护线程和用户线程&#xff0c;通过方法Thread.setDaemon(boolean)&#xff1b;true表…

框架开发有哪些优势?Java主流框架

什么是框架“框架&#xff08;Framework&#xff09;"一词最早出现在建筑领域&#xff0c;指的是在建造房屋前期构建的建筑骨架。在编程领域&#xff0c;框架就是应用程序的骨架&#xff0c;开发人员可以在这个骨架上加入自己的东西&#xff0c;搭建出符合自己需求的应用系…

mac电脑数据恢复?真正实用的方法(2023最新)

使用电脑的用户都知道&#xff0c;被删除的文件一般都会经过回收站&#xff0c;想要恢复它直接点击“还原”就可以恢复到原始位置。mac电脑同理也是这样&#xff0c;但是“回收站”在mac电脑显示为“废纸篓”。 如果电脑回收站&#xff0c;或者是废纸篓里面的数据被清空了&…

Nginx第一讲

目录 一、Nginx01 1.1 Nginx简介 1.1.1 Nginx介绍 1.1.2 Nginx的应用 1.1.3 关于代理 1.1.4 负载均衡 1.1.5 动静分离 1.2 安装Nginx 1.2.1 安装依赖环境 1.2.2 安装nginx 1.2.3 nginx配置文件(nginx.conf) 1.2.4 反向代理实例1 1.2.5 安装tomcat 1.2.6 反向代理…

插画教育培训机构最新排名

学原画插画在哪里学比较好&#xff0c;最新插画培训班排名&#xff0c;给大家梳理了国内最新5家专业的插画师培训班排名&#xff0c;各有优势和特色&#xff0c;给大家借鉴&#xff01; 一&#xff1a;国内插画培训机构排名 1、轻微课&#xff08;五颗星&#xff09; 主打课程有…

flutter 微信通讯录

Flutter 仿制微信通讯录效果&#xff0c;致效果如下&#xff1a; 有几个技术细节&#xff1a; 总体可滑动&#xff0c;少于屏幕长度也可滑动对于数据的处理。昵称 拼音首字母排序&#xff0c;右侧字母导航&#xff0c;点击/滑动&#xff1b;移动到指定位置当点击/滑动 右侧移动…

大数据实操项目分享:餐饮智能推荐服务在线实习项目

项目背景&#xff1a;在“互联网"背景下&#xff0c;餐饮企业的经营方式发生了很大的变革&#xff1a;团购和020拓宽了销售 渠道&#xff0c;电子点餐、店内WIFI等信息技术提升了服务水平&#xff0c;大数据、私人定制更好地满足了细分市场的需求等。但是与此同时&#xf…

天!转转MySQL机房迁移半小时结束战斗?

文章目录1 背景2 迁移方案选择2.1 方案一&#xff1a;扩容主从切换2.2 方案二&#xff1a;级联切换2.3 方案对比3 如何又快又稳完成MySQL机房迁移3.1 提前搭建级联3.2 停服3.3 批量操作自动化&#xff0c;关键步骤解耦3.4 集群分级3.5 切换前、后置检查3.6 灰度切换验证4 写在最…