thread_local使用

thread_local关键字指示对象拥有线程存储期,它能与static或extern结合,但是static不影响存储期。该对象的存储在线程开始时分配,在线程结束时释放。每个线程拥有一个对象实例。

全局变量

#include <iostream>
#include <thread>
#include <mutex>

thread_local int x = 0;
std::mutex t1_mutex;

void print_t1() {
for(int i = 0; i < 3; ++i){
x++;
std::lock_guard<std::mutex> lock(t1_mutex);
std::cout << std::this_thread::get_id() << " " << x << std::endl;
}
}

int main() {
std::thread at1(print_t1);
std::thread at2(print_t1);
at1.join();
at2.join();
return 0;
}
/// result
140636358760192 1
140636358760192 2
140636358760192 3
140636350367488 1
140636350367488 2
140636350367488 3

局部变量

#include <iostream>
#include <thread>
#include <mutex>

std::mutex t1_mutex;

void print_t1() {
for(int i = 0; i < 3; ++i){
thread_local int x = 0;
x++;
std::lock_guard<std::mutex> lock(t1_mutex);
std::cout << std::this_thread::get_id() << " " << x << std::endl;
}
}

int main() {
std::thread at1(print_t1);
std::thread at2(print_t1);
at1.join();
at2.join();
return 0;
}
/// result
140636358760192 1
140636358760192 2
140636358760192 3
140636350367488 1
140636350367488 2
140636350367488 3

thread_local的局部变量没有因为for循环作用域而重新赋值,这是因为线程存储期的变量都是和线程绑定的。可以理解为线程的static变量,不过变量的作用域依旧在本身的作用域外。(在for循环外使用x就会报错)

类成员变量

#include <iostream>
#include <thread>
#include <mutex>

std::mutex test_mutex;

class Test {
public:
Test() {
std::lock_guard<std::mutex> lock(test_mutex);
std::cout << "construct Test" << std::endl;
}
~Test() = default;

thread_local static int cnt;
};

thread_local int Test::cnt = 0;

void print_test() {
Test t;
for(int i = 0; i < 3; ++i){
t.cnt++;
std::lock_guard<std::mutex> lock(test_mutex);
std::cout << std::this_thread::get_id() << " " << t.cnt << std::endl;
}
}

int main() {
std::thread at1(print_test);
std::thread at2(print_test);
at1.join();
at2.join();
return 0;
}

// result
construct Test
140108252534528 1
140108252534528 2
140108252534528 3
construct Test
140108244141824 1
140108244141824 2
140108244141824 3

类成员变量如果声明成thread_local一定要注意,改变量同时一定要使用static修饰。

总结

  • thread_local描述的对象在thread开始时分配,在thread结束时释放。
  • 一般在声明时赋值,在本thread中只执行一次。
  • 描述的对象依旧只在作用域内有效。
  • 描述类成员变量时,必须是s