文章内容全部来自:
【C++入门到进阶 多线程 thread_local 关键字】
【CPU眼里的:thread_local】
简介
thread_local 是一个关键字,它用来修饰变量,并被他修饰的变量有以下特征:
- 它指示对象拥有线程静态存储期
- 线程存储期:对象的存储在线程开始时分配,而在线程结束时析构。每个线程拥有其自身的对象实例。 thread_local 能与 static 和 extern 一同出现,以调整链接。
- 它具有
static
修饰的变量一样的初始化特征和生命周期- 也就是说它的初始化只进行一次
- 需要注意的是他在单独修饰类成员变量时,必须加上
static
测试线程存储周期和类static
特性
我们构建这样的测试代码:
class A
{
public:
A() {
cout << __FUNCTION__ << endl;
}
~A() {
cout << __FUNCTION__ << endl;
}
int v = 0;
void add () {
//static int v = 0;
cout << std::this_thread::get_id() << " "
<< ++v << endl;
cout.flush();
}
};
thread_local A a;
int main () {
a;
return 0;
}
对于我们在main()函数外面初始化类A,我们可以看到只构造了一个A类。
随后我们更改测试代码:
thread_local A a;
void threadFunc() {
a.add();
}
int main () {
thread t1(threadFunc);
thread t2(threadFunc);
t1.join();
t2.join();
a.add();
return 0;
}
可以看到结果我们一共构造了3个A实例,并且都完成析构。
这就是我们的线程静态存储期。
测试 修饰类成员变量加上static
如果你在修饰成员变量的时候,不加static会直接报错。
class A
{
public:
A() {
cout << __FUNCTION__ << endl;
}
~A() {
cout << __FUNCTION__ << endl;
}
thread_local inline static int v = 0;
void add () {
//static int v = 0;
cout << std::this_thread::get_id() << " "
<< ++v << endl;
cout.flush();
}
};
随后我们来验证该类成员变量 v 的线程存储周期:
我们创建了两个子线程,然后再子线程中调用 add() ,也就是让 v自增,并且我们在主线程中也调用 add() ,让 v 自增。按照前面的理论我们应该能得出:三个v的打印必须全部是1:
A a;
void threadFunc() {
// 每个线程第一次访问 thread_local 变量 a 时,都会调用构造函数
a.add();
}
int main () {
thread t1(threadFunc);
thread t2(threadFunc);
t1.join();
t2.join();
a.add();
return 0;
}
//结果如下
// A
// 140127114585664 1
// 140127122978368 1
// 140127122982720 1
// ~A
我们可以看到,A变量全局只有一个被存储在 .data段,被各个线程共享。然后我们开始让v完成自增操作,我们可以看到v打印出来都是1,说明该类的成员变量 v 每个线程都有且仅有一个。