走进C++11(四十五) thread_local

Posted 来条消息

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了走进C++11(四十五) thread_local相关的知识,希望对你有一定的参考价值。

今天说说thread_local 关键字。


说到thread_local就要说到C++存储类说明符。C++ 有如下几种存储类说明符:


auto 自动存储期(C++11前)
register 自动存储期,另提示编译器将此对象置于处理器的寄存器。(弃用)(C++17前)
static 静态线程存储期和内部连接。 
extern 静态线程存储期和外部连接。
thread_local 线程存储期
mutable 不影响存储期或连接


对于C++来说,声明中只可以出现一个存储类说明符,但 thread_local 可以与 static 或 extern 结合 (C++11 起)。 需要注意的是, thread_local 关键词仅允许搭配声明于命名空间作用域的对象、声明于块作用域的对象及静态数据成员。它指示对象具有线程存储期。它能与 static 或 extern 结合,以分别指定内部或外部连接(但静态数据成员始终拥有外部链接),但额外的 static 不影响存储期。


说到这,大家是不是对存储期还有疑问?下面分别讲讲存储期的概念:


  • 自动(automatic)存储期。这类对象的存储在外围代码块开始时分配,并在结束时解分配。未声明为 static、extern 或 thread_local 的所有局部对象均拥有此存储期。

  • 静态(static)存储期。这类对象的存储在程序开始时分配,并在程序结束时解分配。这类对象只存在一个实例。所有声明于命名空间(包含全局命名空间)作用域的对象,加上声明带有 static 或 extern 的对象均拥有此存储期。有关拥有此存储期的对象的初始化的细节,见非局部变量与静态局部变量。

  • 线程(thread)存储期。这类对象的存储在线程开始时分配,并在线程结束时解分配。每个线程拥有其自身的对象实例。只有声明为 thread_local 的对象拥有此存储期。thread_local 能与 static 或 extern 一同出现,它们用于调整连接。关于具有此存储期的对象的初始化的细节,见非局部变量和静态局部变量。

(C++11 起)
  • 动态(dynamic)存储期。这类对象的存储是通过使用动态内存分配函数来按请求进行分配和解分配的。关于具有此存储期的对象的初始化的细节,见 new 表达式。 


下面举一个小例子:


#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 << 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 << "main 的愤怒计数:" << rage << '\n'; }  a.join(); b.join();}


可能的输出:


a 的愤怒计数:2main 的愤怒计数:1b 的愤怒计数:2


可以看出全局的 thread_local 变量在每个线程里是分别自加互不干扰的。


总结一下你需要记住的关于thread_local的知识(划重点):


  1.  对于全局变量, thread_local 变量在每个线程里是分别自加互不干扰的。

  2. 作为局部变量,thread_local 变量会自动 static。

  3. 作为局部变量,thread_local 虽然改变了变量的存储周期,但是并没有改变变量的使用周期或者说作用域

  4. 作为类对象,类对象的使用和创建和内部类型类似,都不会创建多个,thread_local 变量只会在每个线程最开始被调用的时候进行初始化,并且只会被初始化一次。

  5. 作为类成员变量,thread_local变量必须是 static 的。


那么thread_local在现实中有什么用呢?


这就要说到一个概念--线程局部存储(Thread Local Storage,TLS),它是一种存储期(storage duration),对象的存储是在线程开始时分配,线程结束时回收,每个线程有该对象自己的实例。这种对象的链接性(linkage)可以是静态的也可是外部的。


大名鼎鼎的envoy就用到了TLS实现了类似RCU(read copy update)的功能,使系统没有锁。有兴趣的话可以去围观。





以上是关于走进C++11(四十五) thread_local的主要内容,如果未能解决你的问题,请参考以下文章

python第四十五课——继承性之多继承

leetcode刷题四十五

leetcode刷题四十五

学习四十五

销毁 thread_local 对象

线程局部存储: gcc __thread与c++11 thread_local 关键字