引言
线程局部存储在其它语言中都是以库的形式提供的(库函数或类)。但在C++11中以关键字的形式,做为一种存储类型出现,由此可见C++11对线程局部存储的重视。C++11中有如下几种存储类型:
C++11多线程-线程局部存储(thread_local)
thread_local 是 C++ 11 新引入的一种存储类型,它会影响变量的存储周期。
C++ 中有 4 种存储周期:
automatic
static
dynamic
thread
有且只有 thread_local 关键字修饰的变量具有线程(thread)周期,这些变量在线程开始的时候被生成,在线程结束的时候被销毁,并且每一个线程都拥有一个独立的变量实例。 thread_local 一般用于需要保证线程安全的函数中。
需要注意的一点是,如果类的成员函数内定义了 thread_local 变量,则对于同一个线程内的该类的多个对象都会共享一个变量实例,并且只会在第一次执行这个成员函数时初始化这个变量实例,这一点是跟类的静态成员变量类似的。
thread_local修饰的变量具有如下特性: 1、变量在线程创建时生成(不同编译器实现略有差异,但在线程内变量第一次使用前必然已构造完毕)。 2、线程结束时被销毁(析构,利用析构特性,thread_local变量可以感知线程销毁事件)。 3、每个线程都拥有其自己的变量副本。 4、thread_local可以和static或extern联合使用,这将会影响变量的链接属性。
下面用一些测试样例说明:
示例1
#include<iostream>
#include<thread>
using namespace std;
class A
{
public:
A() {}
~A() {}
void test(const std::string& name)
{
thread_local int count = 0;
++count;
std::cout << name << ": " << count << std::endl;
}
};
void func(const std::string& name)
{
A a1;
a1.test(name);
a1.test(name);
A a2;
a2.test(name);
a2.test(name);
}
int main()
{
std::thread t1(func, "t1");
t1.join();
std::thread t2(func, "t2");
t2.join();
return 0;
}
示例2
#include<iostream>
#include<thread>
using namespace std;
class A
{
public:
A() {}
~A() {}
void test(const std::string& name)
{
static int count = 0;
++count;
std::cout << name << ": " << count << std::endl;
}
};
void func(const std::string& name)
{
A a1;
a1.test(name);
a1.test(name);
A a2;
a2.test(name);
a2.test(name);
}
int main()
{
std::thread t1(func, "t1");
t1.join();
std::thread t2(func, "t2");
t2.join();
return 0;
}
示例3
下面代码演示了thread_local变量在线程中的生命周期
#include<iostream>
#include<thread>
using namespace std;
class A
{
public:
A()
{
std::cout << std::this_thread::get_id()
<< " " << __FUNCTION__
<< "(" << (void*)this << ")"
<< std::endl;
}
~A()
{
std::cout << std::this_thread::get_id()
<< " " << __FUNCTION__
<< "(" << (void*)this << ")"
<< std::endl;
}
void doSth()
{
}
};
thread_local A a;
int main()
{
a.doSth();
std::thread t([]()
{
std::cout << "Thread: "
<< std::this_thread::get_id()
<< " entered" << std::endl;
a.doSth();
});
t.join();
return 0;
}
变量a在main线程和t线程中分别保留了一份副本,以下时序图表明了两份副本的生命周期。
|