从上下文切换谈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;
}
输出:
地址不同,说明两个线程里的local_var
变量不是同一个。
但是你说它是局部变量吧,它的生命周期又很长,thread_local
变量的生命周期从线程创建时开始,到线程结束时结束。
其实抽象出它的具体含义就是:
每个线程独立实例: 每个线程对
thread_local
变量都有自己的独立副本,不会与其他线程的副本共享。生命周期:
thread_local
变量的生命周期从线程创建时开始,到线程结束时结束。
那么问题来了,每个线程都有自己的独立副本,在访问的时候又互不影响,这个是怎么实现的呢?这就引出了上下文切换这个概念。
上下文切换
我们先看一下最开始的thread_function对应的汇编代码:
寄存器fs
记录了当前正在运行的线程所有的thread_local
变量所在内存块的首地址。不同的线程,它的上下文也是不同的,所以寄存器fs
的值也是不同的;这样就非常巧妙的通过线程的上下文切换区分了不同线程对应的thread_local;
如图:
假设现在线程1正在执行,操作系统会创建线程1的上下文,上下文一般是指当前寄存器的值,他们包含了当前程序运行的状态,比如栈指针esp,程序计数器eip,在此例中也包含fs,fs记录了当前线程栈上的thread_local变量的起始地址。
同理我们再创建另一个线程2:
线程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