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

news/2024/4/20 3:35:09/文章来源:https://blog.csdn.net/junxuezheng/article/details/128874816

【并发编程十八】线程局部存储(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.luyixian.cn/news_show_72615.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

UI自动化测试、接口测试等自动化测试策略

今天跟大家介绍UI测试、接口测试、单元测试主要内容&#xff0c;以及每种测试花费时间讨论。 UI测试【Selenium】 UI测试是最接近软件真实用户使用行为的测试类型。通常是模拟真实用户使用软件的行为&#xff0c;即模拟用户在软件界面上的各种操作&#xff0c;并验证这些操作对…

从0探索NLP——神经网络

从0探索NLP——神经网络 1.前言 一提人工智能&#xff0c;最能想到的就是神经网络&#xff0c;但其实神经网络只是深度学习的主要实现方式。 现在主流的NLP相关任务、模型大都是基于深度学习也就是构建神经网络实现的&#xff0c;所以这里讲解一下神经网络以及简单的神经网络…

Anaconda和PyCharm的一些安装问题和命令

今天更新了Windows上的Anaconda到2.3.2&#xff0c;PyCharm到2022.3。 ——发现是纯纯的犯贱orz。出了一堆问题。在这里记录一下供后来者参考。 Anaconda安装 将.\anaconda3\Scripts 和.\anaconda3\Library\bin添加到系统环境变量中。 新建环境的目录在.\anaconda3\envs下 N…

【黑盒模糊测试】路由器固件漏洞挖掘实战--AFL++ qemu_mode

前言 很久之前就想写AFL++的qemu_mode了,只是模糊测试专题的文章有些过于耗费时间,加上工作原因导致一直搁置。最近需要出差会用到黑盒模糊测试,所以就当做复习一遍,我记得Fuzzing 101也有一个qemu_mode的练习,有空的话下一篇文章更新吧~ 编写不易,如果能够帮助到你,希望…

linux的文件权限介绍

文件权限 在linux终端输入 ls -lh 出现下面界面 介绍 基本信息 其中的开头代表着文件类型和权限 而 root 和kali 则分别代表用户名和用户组名用户名顾名思义就是这个文件属于哪一个用户用户组是说自己在写好一个文件后&#xff0c;这个文件是属于该用户所有&#xff0c;…

Java中的Stack与Queue

文章目录一、栈的概念及使用1.1 概念1.2 栈的使用1.3 栈的模拟实现二、队列的概念及使用2.1 概念2.2 队列的使用2.3 双端队列(Deque)三、相关OJ题3.1 用队列实现栈。3.2 用栈实现队列。总结一、栈的概念及使用 1.1 概念 栈&#xff1a;一种特殊的线性表&#xff0c;其只允许在…

Linux系统安装MySQL8.0版本详细教程【亲测有效】

首先官网下载安装包&#xff1a;https://downloads.mysql.com/archives/community/ 一、上传到安装服务器 二、解压 tar -xvf mysql-8.0.31-linux-glibc2.12-x86_64.tar.xz三、移动位置并重新命名 mv mysql-8.0.31-linux-glibc2.12-x86_64 /usr/local/mysql四、创建mysql用户…

Docker 如何配置镜像加速

Docker 镜像加速 国内从 DockerHub 拉取镜像有时会遇到困难&#xff0c;此时可以配置镜像加速器。Docker 官方和国内很多云服务商都提供了国内加速器服务&#xff0c;例如&#xff1a; 科大镜像&#xff1a;https://docker.mirrors.ustc.edu.cn/网易&#xff1a;https://hub-…

代码随想录【Day21】| 530. 二叉搜索树的最小绝对差、501. 二叉搜索树中的众数、236. 二叉树的最近公共祖先

530. 二叉搜索树的最小绝对差 题目链接 题目描述&#xff1a; 给你一棵所有节点为非负值的二叉搜索树&#xff0c;请你计算树中任意两节点的差的绝对值的最小值。 示例&#xff1a; 提示&#xff1a;树中至少有 2 个节点。 难点&#xff1a; 解答错误&#xff01;仅考虑了…

【Npde.js】express以及nodemon

express初始Express什么是Express不使用Express可以创建web服务器吗&#xff1f;Express能做什么安装Express监听GET请求和post请求获取URL中携带的查询参数获取URL中携带的动态参数托管静态资源nodemon为什么使用nodemon初始Express 什么是Express 官方给出的概念&#xff0…

Vue3 基础

Vue3 基础 概述 Vue (发音为 /vjuː/&#xff0c;类似 view) 是一款用于构建用户界面的 JavaScript 框架。它基于标准 HTML、CSS 和 JavaScript 构建&#xff0c;并提供了一套声明式的、组件化的编程模型&#xff0c;帮助你高效地开发用户界面。无论是简单还是复杂的界面&…

Java:Java与Python — 编码大战

Java和Python是目前市场上最热门的两种编程语言&#xff0c;因为它们具有通用性、高效性和自动化能力。两种语言都有各自的优点和缺点&#xff0c;但主要区别在于Java 是静态类型的&#xff0c;Python是动态类型的。它们有相似之处&#xff0c;因为它们都采用了“一切都是对象”…

单片机输入输出模式

单片机输入输出模式输入模式模拟输入、浮空输入、上拉输入、下拉输入GPIO输出模式推挽输出、开漏输出、复用推挽输出、复用开漏输出。上下拉电阻上拉电阻下拉电阻输入模式 模拟输入、浮空输入、上拉输入、下拉输入 模拟输入&#xff1a;I/O端口的模拟信号&#xff08;电压信号…

日志收集笔记(架构设计、Log4j2项目初始化、Lombok)

1 架构设计 ELK 技术栈架构设计图&#xff1a; 从左往右看&#xff0c; Beats&#xff1a;主要是使用 Filebeat&#xff0c;用于收集日志&#xff0c;将收集后的日志数据发送给 Kafka&#xff0c;充当 Kafka 的生产者Kafka&#xff1a;高性能消息队列&#xff0c;主要起缓冲…

关于客户背景调查的两个案例,说下我的真实看法

这篇文章我只是想客观陈述下事实&#xff0c;并没有对他人的贬低与对自己的吹捧之意。只是想通过这样两件小事&#xff0c;传递出来一个观点&#xff1a;在外贸业务开发过程中&#xff0c;很多时候正是那些我们内心抗拒&#xff0c;不愿意沉下心去做的事&#xff0c;才给了我们…

C#与三菱PLC MC协议通信,Java与三菱PLC MC协议通信

三菱PLC的MC协议是一种常用的通信协议&#xff0c;用于实现三菱PLC与其他设备之间的通信。以下是一些关于MC协议的基本信息&#xff1a;协议格式MC协议的通信数据格式如下&#xff1a;数据头网络编号PC编号目标模块IO编号目标模块站号本机模块IO编号本机模块站号请求数据长度请…

嵌套走马灯Carousel

Carousel 的应用很广泛&#xff0c;基础用法这里不多做阐述&#xff0c;感兴趣的可以去element-gui了解Carousel 组件。 今天主要是梳理嵌套走马灯的逻辑&#xff0c;背景如下&#xff1a; 需要对项目做一个展示&#xff0c;项目可能有一个或多个&#xff0c;同时一个项目可能…

初探 qiling ( 麒麟 ):开源的二进制分析、高级代码模拟框架

官方介绍&#xff1a; 官网&#xff1a;https://qiling.io/&#xff1a;https://twitter.com/qiling_iogithub 地址&#xff1a;https://github.com/qilingframework/qiling 1、qiling 简介 qiling 是什么 qiling 基于 python 开发&#xff0c;是一个开源的、可模拟多种架构…

Web前端:全栈开发人员的责任

多年来&#xff0c;关于全栈开发人员有很多说法&#xff0c;全栈开发人员是一位精通应用程序全栈开发过程的专业人士。这包括数据库、API、前端技术、后端开发语言和控制系统版本。你一定遇到过前端和后端开发人员。前端开发人员将构建接口&#xff0c;而后端开发人员将开发、更…

狂神说:方法

何为方法方法是语句和集合&#xff0c;一起执行一个功能【实际上方法就是函数&#xff0c;说法不一样而已】定义方法加了static才能被main方法调用修饰符&#xff08;public static&#xff09; 返回类型 方法名&#xff08;参数类型 参数名&#xff09;// main方法public stat…