走进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)的功能,使系统没有锁。有兴趣的话可以去围观。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值