Multithreading in C++

Last Updated : 31 Aug, 2026

Multithreading is a programming technique in which a program is divided into multiple threads that can run concurrently. It helps improve performance and responsiveness by allowing multiple tasks to execute simultaneously while sharing the same memory space.

  • Threads allow multiple tasks to run independently within the same program.
  • Multithreading improves CPU utilization and application performance.
  • The <thread> header provides functions to create, manage, and synchronize threads in C++.

Note: In C++, multithreading support was introduced in C++11 through the <thread> header file.

Example: Creating a Thread

C++
#include <iostream>
#include <thread>
using namespace std;

void task() {
    cout << "Hello from the thread!" << endl;
}

int main() {
    thread t(task);

    t.join();

    cout << "Main thread finished.";

    return 0;
}   

Output
Hello from the thread!
Main thread finished.

Explanation:

  • thread t(task) creates a new thread that executes the task() function.
  • t.join() makes the main thread wait until t finishes.
  • After the thread completes, the main thread prints its message.

Syntax

thread thread_name(callable);

where,

  • thread_name: It is an object of thread class.
  • callable: It is a callable object like function pointer, function object.

Joining a Thread

The join() function waits for a thread to finish its execution. It is commonly used when the calling thread must wait for the completion of another thread.

Syntax:

thread_name.join();

  • The calling thread remains blocked until the specified thread finishes.
  • A thread should be joined only when it is in a joinable state.
  • Calling join() on a non-joinable thread throws a std::system_error.

Checking if a Thread is Joinable

The joinable() function checks whether a thread object represents a thread that can be joined.

Syntax:

thread_name.joinable();

  • Returns true if the thread is joinable.
  • Returns false if the thread is not associated with a joinable thread.

Detaching a thread

The detach() function separates a thread from its std::thread object, allowing it to execute independently.

Syntax:

thread_name.detach();

After a thread is detached, the calling thread does not wait for it to finish.

Note: A detached thread continues running independently, so its lifetime and access to shared resources must be managed carefully.

Getting Thread ID

In Multithreading in C++ each thread has a unique ID which can be obtained by using the get_id() function.

thread_name.get_id();

The get_id() function returns an object representing the thread’s ID

Example: Program using the above operations altogether.

C++
#include <iostream>
#include <thread>
#include <chrono>
using namespace std;

void task1() {
    cout << "Thread 1 is running. ID: " << this_thread::get_id() << "\n";
}

void task2() {
    cout << "Thread 2 is running. ID: " << this_thread::get_id() << "\n";
}

int main() {
    thread t1(task1);
    thread t2(task2);

    // Get thread IDs
    cout << "t1 ID: " << t1.get_id() << "\n";
    cout << "t2 ID: " << t2.get_id() << "\n";

    // Join t1 if joinable
    if (t1.joinable()) {
        t1.join();
        cout << "t1 joined\n";
    }

    // Detach t2 if joinable
    if (t2.joinable()) {
        t2.detach();
        cout << "t2 detached\n";
    }

    // Give detached thread time to complete
    this_thread::sleep_for(chrono::milliseconds(100));

    cout << "Main thread finished.\n";
    return 0;
}

Output
Thread 1 is running. ID: Thread 2 is running. ID: 140737213290176
140737347512000
t1 ID: 140737347512000
t2 ID: 140737213290176
t1 joined
t2 detached
Main thread finished.

Callables in Multithreading

A callable is an object that can be invoked like a function. C++ threads can execute different types of callables, including functions, lambda expressions, function objects, and member functions.

  • Arguments can be passed to a callable when creating a thread.
  • The callable starts executing when the thread is created.

Callable can be divided into 4 categories:

Function Pointer: A function can be a callable object to pass to the thread constructor for initializing a thread.

C++
#include <bits/stdc++.h>
using namespace std;

// Function to be run 
// by the thread
void func(int n) {
    cout << n;
}

int main() {
    
    // Create a thread that runs 
    // the function func
    thread t(func, 4);
    
    // Wait for thread to finish
    t.join();
    return 0;
}

Output
4

Lambda Expression: Thread object can also use a lambda expression as a callable. Which can be passed directly inside the thread object.

C++
#include <iostream>
#include <thread>

using namespace std;

int main() {
    int n = 3;
    
    // Create a thread that runs 
    // a lambda expression
    thread t([](int n){
        cout << n;
    }, n);

    // Wait for the thread to complete
    t.join();
    return 0;
}


Output

3

Function Objects: Function Objects or Functors can also be used for a thread as callable. To make functors callable, we need to overload the operator parentheses operator ().

C++
#include <iostream>
#include <thread>
using namespace std;

// Define a function object (functor)
class SumFunctor {
public:
    int n;
    SumFunctor(int a) : n(a) {}

    // Overload the operator() to 
    // make it callable
    void operator()() const {
        cout << n;
    }
};

int main() {

    // Create a thread using 
    // the functor object
    thread t(SumFunctor(3));

    // Wait for the thread to 
    // complete
    t.join();
    return 0;
}


Output

3

Non-Static and Static Member Function: We can also use thread using the non-static or static member functions of a class. For non-static member function, we need to create an object of a class but it's not necessary with static member functions.

C++
#include <iostream>
#include <thread>

using namespace std;

class MyClass {
public:
    // Non-static member function
    void f1(int num) {
        cout << num << endl;
    }

    // Static member function that takes one parameter
    static void f2(int num) {
        cout << num;
    }
};

int main() {
    
    // Member functions 
    // requires an object
    MyClass obj;
    
    // Passing object and parameter
    thread t1(&MyClass::f1, &obj, 3);
    
    t1.join(); 
    
    // Static member function can 
    // be called without an object
    thread t2(&MyClass::f2, 7);
    
    // Wait for the thread to finish
    t2.join();  

    return 0;
}


Output

3
7

Thread Management

In C++ thread library, various functions are defined to manage threads that can be reused to perform multiple tasks. Some of the are listed below:

Classes/MethodsDescription
join()Waits for a thread to finish its execution.
detach()Allows a thread to execute independently.
mutexProtects shared data from concurrent access.
lock_guardAutomatically locks and unlocks a mutex within a scope.
condition_variableAllows threads to wait for and notify about a condition.
atomicProvides thread-safe operations on shared variables.
sleep_for()Suspends the current thread for a specified duration.
sleep_until()Suspends the current thread until a specified time point.
hardware_concurrency()Returns the number of hardware-supported concurrent threads.
get_idReturns the ID of a thread.

Problems with Multithreading

Multithreading improves the performance and utilization of CPU, but it also introduces various problems:

  • Deadlock: A deadlock occurs when two or more threads are blocked forever because they are each waiting for shared resources that the other threads hold. This creates a cycle of waiting, and none of the threads can proceed.
  • Race Condition: A race condition occurs when multiple threads access shared data simultaneously and at least one thread modifies it, causing unpredictable or incorrect results.
  • Starvation: Starvation occurs when a thread is continuously unable to access shared resources because other threads keep getting priority, preventing it from executing and making progress.

Thread Synchronization

Thread synchronization is the process of controlling multiple threads when accessing shared resources to avoid data inconsistency and race conditions. It ensures that only one thread accesses a critical resource at a time.

  • Synchronization helps maintain data accuracy and consistency in multithreaded programs.
  • Common synchronization tools in C++ include mutexes, locks, and condition variables.

Context switch in multithreading

A context switch occurs when the CPU stops executing one thread and starts executing another thread. The state of the current thread is saved so that execution can continue later from the same point.

  • It enables multiple threads to share CPU time efficiently.
  • Frequent context switching may reduce performance due to overhead.
Comment