thread_local修饰的变量,可以理解为每个线程独有的静态变量

thread_local 的作用

thread_local 是 C++11 引入的一个关键字,它用于声明线程局部存储(TLS)。线程局部存储指的是每个线程都有自己的变量副本,而这些副本在不同线程之间互不干扰。每个线程访问 thread_local 变量时,都会得到该变量的独立副本。

作用

thread_local 的主要作用是确保每个线程都有该变量的独立副本,避免不同线程之间对该变量的竞争和冲突。这在多线程程序中非常重要,特别是当不同线程需要独立的状态或数据时。


如何使用 thread_local

thread_local 可以修饰普通的变量、静态变量、类成员变量等。

语法

1
thread_local type variable_name;

例如:

1
thread_local int counter = 0;  // 每个线程都有自己的独立 counter 变量

示例

1. 基本用法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream>
#include <thread>

thread_local int thread_counter = 0; // 每个线程都有自己的 thread_counter

void increment_counter() {
++thread_counter;
std::cout << "Thread ID: " << std::this_thread::get_id()
<< ", Counter: " << thread_counter << std::endl;
}

int main() {
std::thread t1(increment_counter);
std::thread t2(increment_counter);

t1.join();
t2.join();

// 主线程也可以访问
increment_counter(); // 主线程也有自己的 thread_counter
return 0;
}

输出(可能的)

1
2
3
Thread ID: 140306947039680, Counter: 1
Thread ID: 140306938646976, Counter: 1
Thread ID: 140306941834368, Counter: 1

说明

  • 这个例子展示了 thread_local 变量 thread_counter 的用法。每个线程都拥有 thread_counter 的独立副本,所以主线程和两个子线程中的 thread_counter 是不同的。
  • thread_counter 是线程局部变量,每个线程的修改不会影响其他线程的副本。

thread_local 的重要特点

  1. 每个线程拥有独立副本

    • 每个线程都有变量的独立副本,线程之间互不影响。
    • 即使是静态或全局变量,加上 thread_local 修饰后,也会变成每个线程的局部变量。
  2. 线程生命周期管理

    • 当线程启动时,thread_local 变量会被初始化。
    • 当线程结束时,thread_local 变量的析构函数会被调用,执行必要的清理操作。
  3. 不适用于所有类型的变量

    • thread_local 变量不能是动态分配的(比如通过 new 创建的对象)和指向虚拟基类的指针等。
    • 对象的构造和析构是自动处理的,且 thread_local 变量必须具有静态或线程存储期。

thread_local 的适用场景

  1. 每个线程都有独立状态

    • 例如,记录每个线程的执行状态、计数器、缓存等。
  2. 避免锁竞争

    • 在多线程环境中,通过将数据声明为 thread_local,每个线程有独立的数据副本,避免了锁的竞争,提高并发效率。
  3. 优化线程性能

    • 当线程需要维护一些状态信息时,使用 thread_local 可以减少不必要的同步开销。

与静态局部变量的对比

普通静态局部变量

1
2
3
4
5
void foo() {
static int count = 0;
++count;
std::cout << "Count: " << count << std::endl;
}
  • static 变量在多个函数调用之间保持其值,但它在所有线程之间是共享的,不适合多线程环境。

thread_local 变量

1
2
3
4
5
void foo() {
thread_local int count = 0; // 每个线程有自己的独立副本
++count;
std::cout << "Count: " << count << std::endl;
}
  • thread_local 变量会确保每个线程有自己的副本,避免多线程共享问题。

限制与注意事项

  1. 线程销毁时的清理

    • thread_local 变量的生命周期是与线程绑定的,线程结束时,这些变量会自动销毁,但它们的析构函数只会在对应线程结束时执行。
  2. 初始化顺序

    • thread_local 变量的初始化顺序不如常规静态变量的初始化顺序明确,因此在使用时需要确保其线程安全初始化。
  3. 性能影响

    • 由于每个线程都有自己的副本,可能会带来内存的开销。特别是在多线程数目较大的情况下,可能需要管理的副本会变得比较多。

总结

  • thread_local 关键字允许每个线程都有变量的独立副本,这对于多线程程序中的线程独立数据存储非常有用。
  • 使用 thread_local 可以避免多线程之间的竞争,提高程序性能和线程安全。
  • thread_local 变量的生命周期与线程绑定,在该线程结束时,自动析构。