
C多线程thread_local文章目录C多线程thread_local概念全局变量局部变量类对象类成员变量其他总结概念首先thread_local是一个关键词thread_local是C 11新引入的一种存储期指定符。它会影响变量的存储周期(Storage duration)与它同是存储期指定符的还有以下几个关键字说明备注auto自动存储期cpp11前, “auto int x; ” 在cpp11起错误register自动存储期。指示编译器将此对象置于处理器的寄存器中。cpp17弃用static静态或者线程存储期的内部链接extern静态或者线程存储期的外部链接thread_local线程存储期cpp11起mutable不影响存储期或链接thread_local指示对象拥有线程存储期。也就是对象的存储在线程开始时分配而在线程结束时解分配。每个线程拥有其自身的对象实例。唯有声明为thread_local的对象拥有此存储期。 thread_local 能与 static 或 extern 结合一同出现以调整链接分别指定内部或外部链接详细的可以查阅存储类说明符 - cppreference.com。thread_local关键词只对声明于命名空间作用域的对象、声明于块作用域的对象及静态数据成员允许。举例如下thread_localintx;// 1 A thread-local variable at namespace scopeclassX{staticthread_localstd::string s;// 2 A thread-local static class data member};staticthread_localstd::string X::s;//The definition of X::s is requiredvoidfoo(){thread_localstd::vectorintv;// 3 A thread-local local variable}复制代码下面我们来具体学习一下thread_local的这几种应用场景。全局变量#includeiostream#includethread#includemutexstd::mutex cout_mutex;// 用于多线程打印thread_localintx1;voidfunc(conststd::stringthread_name){for(inti0;i3;i){x;std::lock_guardstd::mutexlock(cout_mutex);std::coutthread[thread_name]: x xstd::endl;}}intmain(){std::threadt1(func,t1);std::threadt2(func,t2);t1.join();t2.join();return0;}复制代码输出thread[t1]:x2thread[t1]:x3thread[t1]:x4thread[t2]:x2thread[t2]:x3thread[t2]:x4复制代码从上输出也确实能看出每个线程都有自己单独的x副本互不干预。局部变量#includeiostream#includethread#includemutexstd::mutex cout_mutex;//方便多线程打印voidfunc(conststd::stringthread_name){for(inti0;i3;i){thread_localintx1;x;std::lock_guardstd::mutexlock(cout_mutex);std::coutthread[thread_name]: x xstd::endl;}}intmain(){std::threadt1(func,t1);std::threadt2(func,t2);t1.join();t2.join();return0;}复制代码输出thread[t1]: x 2 thread[t1]: x 3 thread[t1]: x 4 thread[t2]: x 2 thread[t2]: x 3 thread[t2]: x 4 复制代码thread_local的局部变量没有因为for循环作用域而重新赋值。这是因为线程存储期的变量都是和线程绑定的所以只有第一次声明时被赋值。可以理解为线程专用的static变量。不过变量的作用域依然是在本身的作用域内。比如在for循环外使用x就会编译时错误。类对象#includeiostream#includethread#includemutexstd::mutex cout_mutex;classA{public:A(){std::lock_guardstd::mutexlock(cout_mutex);std::coutinitialize Astd::endl;}~A(){std::lock_guardstd::mutexlock(cout_mutex);std::coutdestroy Astd::endl;}intcounter0;intget_value(){returncounter;}};voidfunc(conststd::stringthread_name){for(inti0;i3;i){thread_localA*anewA();std::lock_guardstd::mutexlock(cout_mutex);std::coutthread[thread_name]: a.counter:a-get_value()std::endl;}}intmain(){std::threadt1(func,t1);std::threadt2(func,t2);t1.join();t2.join();return0;}复制代码输出initialize A thread[t1]:a.counter:0thread[t1]:a.counter:1thread[t1]:a.counter:2initialize A thread[t2]:a.counter:0thread[t2]:a.counter:1thread[t2]:a.counter:2复制代码可以看出虽然在循环中创建了A的实例a但是并没有因为循环创建了多个。这个与局部变量的情况相同创建的实例相对于thread是static的。但是如果没有在声明时进行赋值就不一样了。如#includeiostream#includethread#includemutexstd::mutex cout_mutex;classA{public:A(){std::lock_guardstd::mutexlock(cout_mutex);std::coutinitialize Astd::endl;}~A(){std::lock_guardstd::mutexlock(cout_mutex);std::coutdestroy Astd::endl;}intcounter0;intget_value(){returncounter;}};voidfunc(conststd::stringthread_name){for(inti0;i3;i){thread_localA*a;anewA();std::lock_guardstd::mutexlock(cout_mutex);std::coutthread[thread_name]: a.counter:a-get_value()std::endl;}return;}intmain(){std::threadt1(func,t1);std::threadt2(func,t2);t1.join();t2.join();return0;}复制代码输出initialize A thread[t1]: a.counter:0 initialize A thread[t1]: a.counter:0 initialize A thread[t1]: a.counter:0 initialize A thread[t2]: a.counter:0 initialize A thread[t2]: a.counter:0 initialize A thread[t2]: a.counter:0 复制代码很好理解在循环执行时每次都创建了一个A实例并对a进行赋值。所有一般情况要求我们thread_local对象声明时赋值。类成员变量thread_local作为类成员变量时必须是static的修改代码#includeiostream#includethread#includemutexstd::mutex cout_mutex;classA{public:A(){std::lock_guardstd::mutexlock(cout_mutex);std::coutinitialize Astd::endl;}~A(){std::lock_guardstd::mutexlock(cout_mutex);std::coutdestroy Astd::endl;}thread_localstaticintkey_;intvalue_24;staticintstatic_;};intA::static_36;thread_localintA::key_12;voidfunc(conststd::stringthread_name){A aa;for(inti0;i3;i){aa.key_--;aa.value_--;aa.static_--;std::lock_guardstd::mutexlock(cout_mutex);std::coutthread[thread_name]: key_:aa.key_, value_:aa.value_, static_:aa.static_std::endl;std::coutthread[thread_name]: A::key_:A::key_, value_:aa.value_, static_: A::static_std::endl;}return;}intmain(){std::threadt1(func,t1);std::threadt2(func,t2);t1.join();t2.join();return0;}复制代码输出initialize A thread[t1]: key_:11, value_:23, static_:35 thread[t1]: A::key_:11, value_:23, static_: 35 thread[t1]: key_:10, value_:22, static_:34 thread[t1]: A::key_:10, value_:22, static_: 34 thread[t1]: key_:9, value_:21, static_:33 thread[t1]: A::key_:9, value_:21, static_: 33 destroy A initialize A thread[t2]: key_:11, value_:23, static_:32 thread[t2]: A::key_:11, value_:23, static_: 32 thread[t2]: key_:10, value_:22, static_:31 thread[t2]: A::key_:10, value_:22, static_: 31 thread[t2]: key_:9, value_:21, static_:30 thread[t2]: A::key_:9, value_:21, static_: 30 destroy A 复制代码从上面例子可以看出thread_local作为类成员时也是对于每个thread分别分配了一个。而static则是全局一个。其他本质上thread_local修饰后仍然是一个变量我们依旧能够使用取地址操作者通过引用的方法传递给其他线程对其进行修改#includeiostream#includethreadthread_localinti0;voidfunc(int*p){*p42;}intmain(){i9;std::threadt(func,i);t.join();std::coutistd::endl;}复制代码程序将输出42。另外thread_local变量在第一次使用时初始化如果变量(类)没有被使用。此变量(类)将不会被初始化#includeiostream#includethreadstructA{A(){std::coutinitialized Astd::endl;}~A(){std::coutdeleted Astd::endl;}inti;};thread_localmy_class ss;voiddo_nothing(){}intmain(){std::threadt1(do_nothing);t1.join();}复制代码总结thread-local storage 和static(或者说global) 存储很类似每一个线程都将拥有一份这个数据的拷贝thread_local对象的生命周期从线程开始时开始(对于全局变量)或者首先分配空间。当线程退出的时候对象析构一般在声明时赋值在本thread中只执行一次。当用于类成员变量时必须是static的。参考C11 thread_local用法 - 知乎 (zhihu.com)存储类说明符 - cppreference.com