c++11 中添加了新的关键字thread_local,用来声明新的存储期(线程存储期变量),即线程局部变量。
存储类指定符是名称声明语法的 decl-specifier-seq 的一部分。与名称的作用域一同,它们控制名称的二个独立属性:其“存储期”与其“链接”。auto - 自动存储期(C++11 起)。register - 自动存储期。亦提示编译器将此对象置于处理器的寄存器。C++17 前(弃用),static - 静态或线程存储期和内部链接。extern - 静态或线程存储期和外部链接,thread_local - 线程存储期。
存储期
程序中的所有对象拥有下列存储期之一:
1 自动存储期。对象的存储在外围代码块开始时分配,而在结束时解分配。除了声明为 static 、 extern 或 thread_local 的所有局部对象拥有此存储期。
2 静态存储期。对象的存储在程序开始时分配,而在程序结束时解分配。只存在对象的一个实例。所有声明于命名空间作用域(包含全局命名空间)的对象,加上声明带有 static 或 extern 的对象拥有此存储期。
3 线程存储期。对象的存储在线程开始时分配,而在线程结束时解分配。每个线程拥有其自身的对象实例。唯有声明为 thread_local 的对象拥有此存储期。 thread_local 能与 static 或 extern 一同出现,以调整链接。
4 (C++11 起)动态存储期。通过使用动态内存分配函数,由请求分配和解分配对象的存储。
什么是thread_local,关于thread_local。thread_specific_ptr代表了一个全局的变量,而在每个线程中都各自new一个线程本地的对象交给它进行管理,这样,各个线程就可以各自独立地访问这个全局变量的本地存储版本,线程之间就不会因为访问同一全局对象而引起资源竞争导致性能下降。而线程结束时,这个资源会被自动释放。 C++11的thread_local来自于boost thread_specific_ptr。
#include <iostream>
#include <string>
#include <thread>
#include <mutex>
thread_local unsigned int rage = 1;
std::mutex cout_mutex;
void increase_rage(const std::string& thread_name)
{
++rage; // 在锁外修改 OK ;这是线程局域变量
std::lock_guard<std::mutex> lock(cout_mutex);
std::cout << "Rage counter for " << thread_name << ": " << rage << '\n';
}
int main()
{
std::thread a(increase_rage, "a"), b(increase_rage, "b");
{
std::lock_guard<std::mutex> lock(cout_mutex);
std::cout << "Rage counter for main: " << rage << '\n';
}
a.join();
b.join();
}
/*
Rage counter for a: 2
Rage counter for main: 1
Rage counter for b: 2
*/
我们可以看到,声明为线程存储期的变量,每个线程之间互不影响,通俗的讲我们可以认为每个线程拥有此变量的一个副本,每个线程之前互不影响,其底层的原理Linux中的线程局部存储等提供的api接口。
#include <pthread.h>
// Returns 0 on success, or a positive error number on error
int pthread_key_create (pthread_key_t *key, void (*destructor)(void *));
// Returns 0 on success, or a positive error number on error
int pthread_key_delete (pthread_key_t key);
// Returns 0 on success, or a positive error number on error
int pthread_setspecific (pthread_key_t key, const void *value);
// Returns pointer, or NULL if no thread-specific data is associated with key
void *pthread_getspecific (pthread_key_t key);
应用实例:利用thread_local实现可重入锁
操作系统如何保证每个线程独享一份变量的数据呢?
Linux中的线程局部存储(一) Linux中的线程局部存储(二)
扩展知识:java中同样有一个ThreadLoca类实现了类似的功能,但是其实现却完全不同: