从上下文切换谈thread_local工作原理

news/2025/3/19 21:57:29/文章来源:https://www.cnblogs.com/curiositywang/p/18231696

从上下文切换谈thread_local工作原理

thread_local是什么

熟悉多线程编程的小伙伴一定对thread_local不陌生,thread_local 是 C++11 引入的一种存储类说明符,用于定义每个线程都有其独立实例的变量。每个线程对这些变量有自己的副本,而不共享其他线程的副本。这在多线程编程中非常有用,确保线程之间的数据隔离,防止数据竞争。

但是thread_local的表现又很奇怪,你说它是全局变量吧,但它又被每个线程私有,比如下面这个例子:

#include <iostream>
#include <thread>
#include <stdio.h>// 定义一个 thread_local 变量
thread_local int local_var = 0;void thread_function(int id) {local_var = id;printf("%x\n", &local_var);
}int main() {std::thread t1(thread_function, 1);std::thread t2(thread_function, 2);t1.join();t2.join();return 0;
}

输出:

image-20240604141151341

地址不同,说明两个线程里的local_var变量不是同一个。

但是你说它是局部变量吧,它的生命周期又很长,thread_local 变量的生命周期从线程创建时开始,到线程结束时结束。

其实抽象出它的具体含义就是:

每个线程独立实例: 每个线程对 thread_local 变量都有自己的独立副本,不会与其他线程的副本共享。

生命周期: thread_local 变量的生命周期从线程创建时开始,到线程结束时结束。

那么问题来了,每个线程都有自己的独立副本,在访问的时候又互不影响,这个是怎么实现的呢?这就引出了上下文切换这个概念。

上下文切换

我们先看一下最开始的thread_function对应的汇编代码:

image-20240604143232421 image-20240604143356925

寄存器fs记录了当前正在运行的线程所有的thread_local变量所在内存块的首地址。不同的线程,它的上下文也是不同的,所以寄存器fs的值也是不同的;这样就非常巧妙的通过线程的上下文切换区分了不同线程对应的thread_local;

如图:

image-20240604143849664

假设现在线程1正在执行,操作系统会创建线程1的上下文,上下文一般是指当前寄存器的值,他们包含了当前程序运行的状态,比如栈指针esp,程序计数器eip,在此例中也包含fs,fs记录了当前线程栈上的thread_local变量的起始地址。

同理我们再创建另一个线程2:

image-20240604145238079

线程1和线程2对应着程序运行栈上的不同区域,因此他们的fs寄存器的值也是不同的。所以当发生线程上下文切换的时候,操作系统会恢复将要运行的线程的上下文(寄存器状态)。这样每个线程也就能自然通过fs对应到它的thread_local变量。

总结

本篇博文先是介绍了thread_local的特性:

  • 每个线程独立实例: 每个线程对 thread_local 变量都有自己的独立副本,不会与其他线程的副本共享。

  • 生命周期: thread_local 变量的生命周期从线程创建时开始,到线程结束时结束。

然后通过上下文切换的概念介绍了实现的方法。有关更多上下文切换的内容,可参照《CPU眼中的C++》5.8节。

refer

《CPU眼中的C++》

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

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

相关文章

POR

上电复位(POR)电路,其作用是保证在施加电源后,模拟和数字模块初始化至已知状态。基本上电复位(POR)功能会产生一个内部复位脉冲以避免"竞争"现象,并使器件保持静态,直至电源电压达到一个能保证正常工作的阈值。注意,此阈值电压不同于数据手册中给出的最小电…

C++U7-07-图的遍历进阶

学习目标 引例 深搜遍历 [【图的遍历进阶】有向图中的可达]【算法分析】 从 a 点广搜,并用 vis 数组标记从 a 能够到达的点,如果 vis b ​=true,则表示能够到达,否则反之。【参考代码】 #include<bits/stdc++.h> using namespace std; const int maxn = 2e5 + …

第2次总结性Blog-题目集4~6

目录前言 设计与分析 采坑心得 改进建议 总结关于java&面向对象在经过这几个月的系统性的java学习中,我始终牢记着一句话:面对对象程序设计最重要的是设计,而不是代码。设计即要遵守单一职责原则,简单来说就是什么该做,什么不该做。设计的越好,复用性就越高,需要修改…

一款WPF的精简版MVVM框架——stylet框架的初体验(包括MVVM绑定、依赖注入等操作)

今天偶然知道一款叫做stylet的MVVM框架,挺小巧的,特别是它的命令触发方式,简单粗暴,让人感觉很巴适,现在我做一个简单的demo来顺便来分享给大家。 本地创建一个WPF项目,此处我使用.NET 8来创建。然后引用stylet最新的nuget包。 然后删掉App.xaml里面自带的启动项删掉以后…

按键中断驱动程序-poll机制

前言:学东西的时候总是喜欢碎碎念,去思考该怎么学。关于嵌入式开发,以前就觉得嵌入式只是一个工具,关键还是结合专业知识赋能,比如控制、信号处理、神经网络、计算机网络、各种协议比如蓝牙、wifi,音视频,当然,如果能够把内核学的很透彻,那也是很了不起的。现在越学越…

UDP协议的应用——域名解析

设计程序实现解析www.baidu.com 的域名,把获取到的百度的IP地址全部输出到终端并验证是否正确设计程序实现解析www.baidu.com 的域名,把获取到的百度的IP地址全部输出到终端并验证是否正确 /******************************************************************************…

基于腾讯元器搭建前端小助手

通过本文,我们了解了如何利用腾讯元器搭建一个前端助手智能体。通过使用插件和观察其使用效果,我们可以发现前端助手在解决问题和提供帮助方面的潜力。这个前端助手可以成为我们在前端开发过程中的得力助手,帮助我们提高工作效率和解决难题。随着智能技术的不断进步,我们可…

根据域名获取IP

/**************************************************************************************************************************************** file name:multicast.c* author :momolyl@126.com* date :2024/06/04* brief :设计程序实现解析www.baidu.co…

npm下载依赖报错:operation not permitted

npm下载依赖报错:operation not permitted 想下个SoybeanAdmin来用下,需要用到pnpm,但是用npm下载的时候直接报错简单来说就是权限的问题,网上很多都说改nodejs下面的几个文件夹的权限,我的评价是不好使,还得修改nodejs这个文件夹的权限才行,右键属性-安全-编辑-勾上完全…

containerd 源码分析:创建 container(三)

文接 containerd 源码分析:创建 container(二) 1.2.2.2 启动 task 上节介绍了创建 task,task 创建之后将返回 response 给 ctr。接着,ctr 调用 task.Start 启动容器。 // containerd/client/task.go func (t *task) Start(ctx context.Context) error {r, err := t.client…