What does thread_local mean in C ++ 11?

I am confused with the description of thread_local in C ++ 11. I understand that each thread has a unique copy of local variables in the function. Global / static variables can be accessed by all threads (possibly synchronized access using locks). And thread_local variables are visible to all threads, but can only be changed by the thread for which they are defined? Is it correct?

+107
c ++ multithreading c ++ 11 thread-local-storage thread-local
Aug 16 '12 at 9:05
source share
3 answers

Duration of storage over a local network is a term used to refer to data that seems to be global or static storage duration (in terms of functions using it), but in fact there is one instance for the stream.

It adds to the current automatic (exists during the block / function), static (exists for the duration of the program) and dynamic (exists on the heap between allocation and deallocation).

What is thread-local is created when the thread is created and deleted when the thread stops.

The following are some examples.

Think of a random number generator where the seed should be supported based on the flow. Using a local thread-stream means that each thread gets its own sequence of random numbers, regardless of other threads.

If your seed was a local variable inside a random function, it will be initialized every time you call it, each time indicating the same number. If it was global, flows will interfere with each other.

Another example is something like strtok , where the tokenization state is stored depending on the stream. Thus, one thread can be sure that other threads will not eliminate their tokenization actions, while maintaining the state of several calls to strtok - this basically makes the backup version of strtok_r (thread-safe version) redundant.

Both of these examples allow the existence of a local stream variable within the function used. In pre-threaded code, this will simply be a variable of static storage duration in the function. For streams that change for local storage localization.

Another example is something like errno . You do not want individual threads to modify errno after one of your calls fails, but before you can check the variable, and yet you only want one instance per thread.

This site contains a reasonable description of the various storage duration specifications.

+127
Aug 16 2018-12-12T00:
source share

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 ".

+116
Aug 16 2018-12-12T00:
source share

Stream local memory is stored in all aspects, such as static (= global), only for each stream there is a separate copy of the object. The lifetime of an object starts either at the start of the stream (for global variables) or at initial initialization (for local-local statics) and ends when the stream ends (i.e., when join() called).

Therefore, only variables that can also be declared static can be declared as thread_local , that is, global variables (more precisely: variables in the namespace), static members of the class, and block static variables (in which case is static ).

As an example, suppose you have a thread pool and want to know how balanced your workload is:

 thread_local Counter c; void do_work() { c.increment(); // ... } int main() { std::thread t(do_work); // your thread-pool would go here t.join(); } 

This will print thread usage statistics, for example. with implementation as follows:

 struct Counter { unsigned int c = 0; void increment() { ++c; } ~Counter() { std::cout << "Thread #" << std::this_thread::id() << " was called " << c << " times" << std::endl; } }; 
+20
Aug 16 2018-12-12T00:
source share



All Articles