Fundamentals 10 min read

Mastering std::thread: Create, Manage, and Synchronize C++ Threads

This tutorial explains the fundamentals of C++11 std::thread, covering thread creation with functions and lambdas, the use of join() and detach(), argument passing techniques, and essential best practices for safe and portable multithreaded programming.

php Courses
php Courses
php Courses
Mastering std::thread: Create, Manage, and Synchronize C++ Threads

In modern computer systems, multi‑core processors are mainstream. To fully utilize hardware resources, writing concurrent programs is essential. Before C++11, multithreading required OS‑specific APIs, which were cumbersome and non‑portable. C++11 introduced the <thread> header, providing the std::thread class for powerful and portable multithreading support. This section explores the basics of std::thread to help you start C++ concurrent programming.

1. What is std::thread?

std::thread

is a class representing a single execution thread. Each std::thread object represents an independent thread. You can create multiple std::thread objects to run several functions concurrently.

2. Creating your first thread

Creating a new thread is simple: construct a std::thread object and pass the callable (function, functor, lambda, etc.) you want to run in the new thread as an argument.

Example 1: Using a function

#include <iostream>
#include <thread>

// This function will be executed in a new thread
void helloFunction()
{
    std::cout << "Hello from thread (function)!
";
}

int main()
{
    // Create a new thread t and immediately execute helloFunction
    std::thread t(helloFunction);
    // Main thread continues its work
    std::cout << "Hello from main thread!
";

    // Wait for thread t to finish
    t.join();

    return 0;
}

Possible output:

Hello from main thread!
Hello from thread (function)!

Or

Hello from thread (function)!
Hello from main thread!

Note: The output order is nondeterministic because it depends on the OS scheduler, illustrating thread concurrency.

Example 2: Using a lambda expression (more modern)

#include <iostream>
#include <thread>

int main()
{
    // Create thread using a lambda
    std::thread t([](){
        std::cout << "Hello from thread (lambda)!
";
    });
    std::cout << "Hello from main thread!
";
    t.join();
    return 0;
}

3. Waiting for a thread to finish: join()

join()

is a key member function of std::thread. It blocks the current thread (usually the main thread) until the called thread ( t) completes. In the examples above, t.join() blocks main until helloFunction or the lambda finishes.

Why must you call join()?

The C++ standard mandates that if a std::thread object is still joinable (created but not joined or detached) when it is destroyed, std::terminate() is invoked, terminating the program. Therefore you must call join() or detach() before the thread object's lifetime ends.

4. Letting a thread run independently: detach()

Unlike join(), detach() separates the thread. After calling detach():

The main thread and the child thread are completely independent.

The child becomes a daemon thread running in the background.

The main thread can no longer interact with the std::thread object.

When the child finishes, its resources are reclaimed automatically.

Example:

#include <iostream>
#include <thread>
#include <chrono>

void independentWorker()
{
    std::this_thread::sleep_for(std::chrono::seconds(2));
    std::cout << "Worker thread done after sleeping.
";
}

int main()
{
    std::thread t(independentWorker);
    t.detach(); // Let the thread run on its own
    std::cout << "Main thread is done. Exiting.
";
    // Note: The background thread may still be running after main exits.
    std::this_thread::sleep_for(std::chrono::seconds(3)); // Wait to see output
    return 0;
}

Using detach() requires extreme care because you lose direct control over the thread and must ensure any data accessed by the thread remains valid for its lifetime.

5. Passing arguments to thread functions

Passing arguments to a thread function works like a normal function call: list the arguments after the callable.

Example:

#include <iostream>
#include <thread>
#include <string>

void printMessage(const std::string& msg, int id)
{
    std::cout << "Thread " << id << ": " << msg << '
';
}

void increment(int& x)
{
    ++x;
}

int main()
{
    std::string message = "Hello, World!";
    int threadId = 1;

    // 1. Pass by value (default)
    std::thread t1(printMessage, message, threadId);
    t1.join();

    int value = 10;
    // 2. Pass by reference using std::ref
    std::thread t2(increment, std::ref(value));
    t2.join();
    std::cout << "Value after increment: " << value << '
';

    // 3. Move-only types using std::move
    std::unique_ptr<int> ptr(new int(42));
    std::thread t3([](std::unique_ptr<int> p){
        std::cout << "Value in thread: " << *p << '
';
    }, std::move(ptr));
    t3.join(); // ptr is now empty in the main thread

    return 0;
}

Key points:

Default is value copy, giving each thread its own data copy.

To pass a reference, wrap the argument with std::ref() or std::cref().

To pass move‑only types, use std::move() to transfer ownership.

6. Core takeaways and best practices

RAII for threads: always call join() or detach() before a std::thread object is destroyed; consider a wrapper class to automate this.

Prefer join() over detach() unless you have a strong reason; it gives better control over thread lifetime.

Beware data races: protect shared data with synchronization primitives such as std::mutex (to be covered later).

Threads are non‑copyable; they can only be moved with std::move, reflecting exclusive ownership of thread resources.

Conclusion

std::thread

is the cornerstone of C++ multithreading. After this section you should understand how to create threads with functions and lambdas, the differences and usage of join() and detach(), how to pass arguments (by value, reference, or move), and basic best practices and cautions.

Future sections will explore stronger synchronization primitives such as std::mutex, std::atomic, and advanced thread‑management techniques to help you build robust and efficient multithreaded C++ applications.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

concurrencyLambdaCmultithreadingJOINstd::threaddetach
php Courses
Written by

php Courses

php中文网's platform for the latest courses and technical articles, helping PHP learners advance quickly.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.