When you declare a thread_local variable, each thread has its own copy. When you refer to it by name, the copy associated with the current stream is used. eg.
thread_local int i=0; void f(int newval){ i=newval; } void g(){ std::cout<<i; } void threadfunc(int id){ f(id); ++i; g(); } int main(){ i=9; std::thread t1(threadfunc,1); std::thread t2(threadfunc,2); std::thread t3(threadfunc,3); t1.join(); t2.join(); t3.join(); std::cout<<i<<std::endl; }
This code will output "2349", "3249", "4239", "4329", "2439" or "3429", but never again. Each thread has its own copy of i , which is assigned, incremented and then printed. The main stream also has its own copy, which is assigned at the beginning, and then remains unchanged. These copies are completely independent, and each has a different address.
In this respect, only the name is used - if you take the address of the thread_local variable, then you have a regular pointer to a regular object that you can freely pass between threads. eg.
thread_local int i=0; void thread_func(int*p){ *p=42; } int main(){ i=9; std::thread t(thread_func,&i); t.join(); std::cout<<i<<std::endl; }
Since the address i is passed to the thread function, then a copy of i belonging to the main thread can be assigned even if it is thread_local . Thus, this program will output "42". If you do this, then you need to make sure that *p not accessible after exiting the stream to which it belongs, otherwise you will get a dangling pointer and undefined behavior, as in any other case, when an object with a pointer destroyed.
thread_local variables are initialized "before first use", so if they are never touched by this thread, they are not necessarily initialized. This allows compilers not to construct each thread_local variable in a program for a thread that is completely autonomous and does not apply to any of them. eg.
struct my_class{ my_class(){ std::cout<<"hello"; } ~my_class(){ std::cout<<"goodbye"; } }; void f(){ thread_local my_class; } void do_nothing(){} int main(){ std::thread t1(do_nothing); t1.join(); }
There are 2 threads in this program: the main thread and the manually created thread. None of the threads call f , so the thread_local object thread_local never used. Therefore, it is unclear whether the compiler will create 0, 1, or 2 instances of my_class , and the output may be "," hellohellogoodbyegoodbyoe "or" hellogoodbye ".