首页 > 其他分享 >从上下文切换谈thread_local工作原理

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

时间:2024-06-04 20:55:21浏览次数:24  
标签:fs 变量 thread 线程 上下文 local

从上下文切换谈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++》

标签:fs,变量,thread,线程,上下文,local
From: https://www.cnblogs.com/curiositywang/p/18231696

相关文章

  • 46.ThreadPoolExcutor接口
    线程池状态ThreadPoolExcutor使用int高3位来表示线程池状态,低29位表示线程数量状态高三位接收新任务处理阻塞队列任务说明RUNNING111YYSHUTDOWN000NY不会接收新任务,但会处理阻塞队列剩余任务,比较温和,已经提交的任务都会执行。STOP001NN会中断正在执行的任务,并抛弃阻塞队列任......
  • rt-thread 系统pm组件在4.1.1版本的无法正常唤醒的问题解决方法
    在老的rt-thread版本系统pm组件调试ok,后来使用4.1.1版本时发现进入低功耗后无法正常唤醒,问题解决路径如下硬件信息:cpu STM32L431CCT6新建系统打开pm组件后也没有drv_pm.c和drv_lptim.c自动添加,需要到系统目下找到并复制到driver目录下C:\RT-ThreadStudio\repo\Extract\R......
  • TransmittableThreadLocal原理浅析
    TransmittableThreadLocal原理浅析1.从ThreadLocal到TransmittableThreadLocalTTL(TransmittableThreadLocal)是继承自InheritableThreadLocal,本质上也是一个ThreadLocal,可以理解为是一个可以在线程之间传递上下文的ThreadLocal。1.1ThreadLocalThreadLocal中填充的变量属于当......
  • 原子上下文和中断上下文的区别
    原子上下文(AtomicContext)定义:原子本意是“不能被进一步分割的最小粒子”,而原子操作指的是“不可被中断的一个或一系列操作”。在操作系统中,原子上下文通常与原子操作相关,这些操作在执行过程中不会被其他操作或中断打断,从而保证其完整性。特点:不可分割性:原子操作在执行过程中......
  • rt-thread AT组件偶尔死机的问题
    硬件信息单片机STM32L431CCT64G模组 EC800K-CNrtt版本:4.1.1  第一个问题主频太低不行,比如使用外部晶振8M时会发现at命令的返回收到的数据不完整,是由于处理器太慢和rt-thread系统处理工作较多导致,测试发现至少16M主频以上才能良好运行 第二个问题频繁通过串口......
  • ThreadLocal使用过程中要注意哪些事项
    在使用ThreadLocal过程中,需要注意以下几个关键事项以确保正确和高效地使用这一工具:内存泄漏预防:ThreadLocal变量在不再使用时应及时调用remove()方法清理,避免因为ThreadLocal对象的引用链没有断开而导致的内存泄漏。特别是在线程池环境中,线程会被复用,如果不清理,之前线程绑定......
  • Docker + maven build problem — unix://localhost:80: Permission denied
    使用docker-maven-plugin进行构建镜像报错如下:com.spotify.docker.client.shaded.org.apache.http.impl.execchain.RetryExecexecuteINFO:I/Oexception(java.io.IOException)caughtwhenprocessingrequestto{}->unix://localhost:80:Permissiondenied解决方案:Ad......
  • SpringBoot之ThreadLocal保存请求用户信息
    一、ThreadLocal概述 线程局部变量,创建一个线程变量后,针对这个变量可以让每个线程拥有自己的变量副本,每个线程是访问的自己的副本,与其他线程的相互独立。二、具体代码demo实现(1)创建user实例对象@DatapublicclassUserDTO{privateLonguserId;privateStringU......
  • 翻译《The Old New Thing》- Consequences of the scheduling algorithm: Low priorit
    Consequencesoftheschedulingalgorithm:Lowprioritythreadscantake100%CPU-TheOldNewThing(microsoft.com)https://devblogs.microsoft.com/oldnewthing/20071220-00/?p=24093 RaymondChen 2007年12月20日调度算法的控制:低优先级线程也可能占用100%的......
  • [SQL DRIVEN] GetExistingLocalJoinPath
    ThefollowingsqlstatementswilltriggercallingfunctionGetExistingLocalJoinPath.setupCREATEEXTENSIONpostgres_fdw;CREATESERVERforeign_serverFOREIGNDATAWRAPPERpostgres_fdwOPTIONS(host'127.0.0.1',dbname'postgres',por......