Updated June 30, 2023
Introduction to C++ thread_local
In C++, the thread_local specifier is used to define thread-local data, which means that each thread has its own copy of the data. This data is created when the thread is created and destroyed when the thread is terminated. This thread-local data is stored in what is known as thread-local storage. The thread_local specifier is one of the storage class specifiers in C++, along with extern and static. In C++, we use the thread_local specifier to declare a variable as thread-local. When a thread is created, it creates a new copy of the variable specific to that thread. It is important to note that the thread_local specifier can only be applied to variables and cannot be used with function definitions or declarations.
Working of thread_local in C++
In this article, we will discuss thread_local in C++. Thread-local data, also known as thread-specific data, is a type of data that is associated with each individual thread. When a variable is declared with the thread_local specifier, it is stored in thread-local storage. This storage is allocated on a per-thread basis, meaning that each thread has its own copy of the variable.
The initialization of such variables requires a static constructor, and if this thread_local variable with a namespace or class scope can be initialized as a part of thread startup and it is static only when a member of the class can only be thread-local and hence each variable can have one copy per thread. Whereas such initialized thread-local variables are allocated in .tdata sections, and uninitialized ones are stored as variables defined with a “COMMON” symbol, and for each new thread created or initialized, the thread allocated a new block in thread-local storage with each thread having a thread pointer that points to the control block of thread and has the value of pointer of thread pointer of the present executing thread. So the thread-local storage can be created only when any new thread is created or when a thread refers to any thread-local storage block for the very first time after shared objects are loaded or at the program startup itself.
Now let us see an example of declaring variables as thread_local, where it will have its own threads copy, and we can refer to it by its name. Then the current thread is used when this copy of each thread is associated. So let us see in the below example the variables can be declared thread_local for integer and string data types.
Example of C++ thread_local
Below is an example:
Code:
#include <iostream>
#include <string>
#include <mutex>
#include <thread>
std::mutexcoutMutex;
thread_localint n=2;
thread_localstd::string str("hello Educba from thread number and address of the string is");
void thread_integer(intn_val){
n=n_val;
}
void thread_cnt(){
std::cout<<n;
}
void thread_func(int td){
thread_integer(td);
++n;
thread_cnt();
}
void thread_string(std::string const& a2){
str+=a2;
std::lock_guard<std::mutex> guard(coutMutex);
std::cout<<str<<std::endl;
std::cout<< "&str: " <<&str<<std::endl;
std::cout<<std::endl;
}
intmain(){
n=4;
std::thread it1(thread_func,1);
std::thread it2(thread_func,2);
std::thread it3(thread_func,3);
it1.join();
it2.join();
it3.join();
std::cout<<n<<std::endl;
std::cout<<std::endl;
std::thread tt1(thread_string,"thread number 1");
std::thread tt2(thread_string,"thread number 2");
std::thread tt3(thread_string,"thread number 3");
std::thread tt4(thread_string,"thread number 4");
tt1.join();
tt2.join();
tt3.join();
tt4.join();
}
Output:
In the provided program, the ‘thread’ library is included to utilize the thread_local specifier. The ‘iostream’ library is included to facilitate input and output operations. The ‘string’ library is included to declare and manipulate string variables. Lastly, the ‘mutex’ library is included to ensure the protection of shared data that multiple threads can access. Then we declared two variables, one of integer type named “n” and another of string type “str,” and both these variables we are declaring as thread_local to see the output. We have already given the initial values for both types in this declaration. For integer, we start from the thread “2,” and for string type, we print the message in the str value. Then we will write functions for reading the integer values until specified in the main function. Therefore as seen in the above screenshot, when we are printing the threads of integer type starting from thread number 2 to thread number 4, we are displaying only 3 threads, so in the output, we can see first thread 2 is executed, then thread 4, thread 3 and again it will execute thread 4. So it may vary while running this program. Hence we can see here each thread has its own copy. So in the above program, we can say we can match this with a random number generator; here, each thread has its own random number of the thread in sequence, irrespective of other threads.
Conclusion
In this article, we have seen one simple example of creating threads by declaring the int type and string type variables as thread_local, which requires a thread library and mutex library for displaying the threads executing and having their own copy irrespective of other threads.
Recommended Articles
This is a guide to C++ thread_local. Here we also discuss the definition and working of C++ thread_local, different examples, and code implementation. You may also have a look at the following articles to learn more –