前言
在内存模型之上,C++提供了线程加锁的并发模型来消除数据竞争(比如全局数据。数据竞争可能产生极为隐晦的并发错误)。但是,线程加锁级别的并发是应用程序使用并发的比较差的模型。
C++11引入了thread_local来保证线程访问全局数据的安全,thread_local告诉线程进行数据本地存储(线程本地存储一个数据备份),从而消除线程间数据竞争的一种方式。
thread_local概念
thread_local是C++11引入的新特性,是一个关键字,用于修饰变量(thread_local关键字和static、extern关键字在使用上是不冲突的),告诉线程对此关键词修饰的数据进行本地存储。
使用的头文件:#include< thread>
thread_local与“线程加锁”的区别
thread_local:把全局数据拷贝出一份自己使用,从进程内部所有线程共用变成线程内部私有。随后线程使用的变量就是这个备份数据,不再关心原全局数据的数值。
(进程内部的数据在那里,只是系统给当前线程分配了同样类型同样名称的变量,专供本线程使用。)
线程加锁的方式:全局数据进程内部所有线程共用,每个线程都可以对这个数据进行读和写,为了保证数据同一时间只能有一个修改,所以加锁进行限制。
thread_local应用场景
为了防止变量值的不可预测性,保证各线程内变量值的互不干扰,想要达到线程内独有的全局变量的场合。
举例(仅供参考):
①修饰全局业务唯一性的某个全局变量A,在fun_A函数中进行A的赋值(比如,随机数、随机串、线程池个数等赋值计算)。
以前当前进行内部所有线程都共用A和fun_A,但是根据业务扩展需要,每个线程内部都需要维护一份业务唯一性数据。但是业务特殊性导致线程之间业务差异比较大,无法通过拷贝多个进程实现多种业务。
所以为了修改方便,可以选择对A的变量增加thread_local修饰的方案。
thread_local的使用范例
thread_local主要是用来修饰变量,修饰变量的种类有以下四种:
1)全局变量
2)局部变量
3)类对象
4)类成员变量
下面我们以全局变量和局部变量为例,来说明thread_local修饰变量的效果。
示例代码:
#include <iostream>
#include <thread>
int g_a = 10;
thread_local int g_b = 20;
static int g_c = 30;
thread_local static int g_d = 40;
void fun(const std::string& name)
{
for (int i = 0; i < 3; ++i)
{
thread_local int temp = 1; //只在每个线程创建时初始化一次
temp++;
std::cout