C++ thread_local Keyword

The thread_local keyword in C++ is used to declare variables with thread storage duration. This means that each thread has its own, independent instance of the variable, ensuring that data is not shared between threads.

Variables declared with thread_local are useful in multithreaded programming, where threads need their own copies of variables to avoid race conditions or unexpected data sharing. This keyword was introduced in C++11.


Syntax

</>
Copy
thread_local data_type variable_name = initial_value;
thread_local
Specifies that the variable has thread storage duration, meaning each thread gets its own instance.
data_type
The type of the variable.
variable_name
The name of the variable.
initial_value
An optional value used to initialize the variable.

Examples

Example 1: Using thread_local with a Simple Variable

In this example, you will learn the behavior of a thread_local variable in a multithreaded environment.

</>
Copy
#include <iostream>
#include <thread>

using namespace std;

thread_local int threadLocalVar = 0;

void incrementAndPrint(const string& threadName) {
    threadLocalVar++;
    cout << threadName << ": threadLocalVar = " << threadLocalVar << endl;
}

int main() {
    thread t1(incrementAndPrint, "Thread 1");
    thread t2(incrementAndPrint, "Thread 2");

    t1.join();
    t2.join();

    return 0;
}

Output:

Thread 1: threadLocalVar = 1
Thread 2: threadLocalVar = 1

Explanation:

  1. The variable threadLocalVar is declared as thread_local, so each thread has its own copy.
  2. Both threads increment their own instance of threadLocalVar, resulting in separate values for each thread.
  3. There is no interference or shared state between threads, ensuring thread safety.

Example 2: Using thread_local with a Function Static Variable

This example shows how thread_local affects static variables within a function.

</>
Copy
#include <iostream>
#include <thread>

using namespace std;

void updateStaticVar(const string& threadName) {
    thread_local static int count = 0; // Each thread gets its own static variable
    count++;
    cout << threadName << ": count = " << count << endl;
}

int main() {
    thread t1(updateStaticVar, "Thread 1");
    thread t2(updateStaticVar, "Thread 2");

    t1.join();
    t2.join();

    return 0;
}

Output:

Thread 1: count = 1
Thread 2: count = 1

Explanation:

  1. The count variable is declared as both static and thread_local.
  2. Each thread has its own static instance of count, which is initialized and updated independently.
  3. Despite being static, count is not shared across threads due to the thread_local specifier.

Example 3: Using thread_local with Complex Data Types

This example demonstrates a thread_local variable with a custom class type.

</>
Copy
#include <iostream>
#include <thread>
#include <string>

using namespace std;

class Logger {
    string threadName;

public:
    Logger(const string& name) : threadName(name) {}

    void log(const string& message) {
        cout << "[" << threadName << "] " << message << endl;
    }
};

thread_local Logger logger("Default");

void threadFunction(const string& threadName) {
    logger = Logger(threadName); // Initialize the logger for this thread
    logger.log("Starting thread work.");
    logger.log("Finishing thread work.");
}

int main() {
    thread t1(threadFunction, "Thread 1");
    thread t2(threadFunction, "Thread 2");

    t1.join();
    t2.join();

    return 0;
}

Output:

[Thread 1] Starting thread work.
[Thread 1] Finishing thread work.
[Thread 2] Starting thread work.
[Thread 2] Finishing thread work.

Explanation:

  1. The logger object is declared as thread_local, so each thread gets its own independent instance.
  2. The object is initialized with a unique name for each thread, ensuring thread-specific behavior.
  3. Each thread executes its logging without interference from other threads.

Key Points to Remember about thread_local Keyword

  1. The thread_local keyword ensures that each thread has its own instance of a variable.
  2. Variables with thread_local storage duration are initialized once per thread and destroyed when the thread exits.
  3. It can be combined with static or const, but not with extern.
  4. Useful for thread-specific storage without needing external synchronization mechanisms.
  5. Available in C++11 and later versions.