In C++, concurrency allows your application to execute multiple tasks simultaneously, utilizing all available CPU cores. While older languages run within single threaded execution models or hide threads behind virtual runtimes, C++ gives you direct control over hardware threads.
Let's look at basic thread spawning, stack frames, and thread destruction rules.
Threads: Separate Execution Paths
When you run a standard C++ program, it executes sequentially on a single thread called the main thread. By importing the <thread> library, we can spawn new threads. Each thread represents a separate call stack executing a function concurrently:
Thread Lifetime Rules: Join vs Detach
When you instantiate a std::thread object, it begins executing immediately. However, you must explicitly manage its lifetime. If the thread object goes out of scope and you have not decided how to handle it, the destructor of std::thread will call std::terminate, crashing your entire application immediately.
You have two options to manage thread lifetimes:
* join(): This blocks the current thread (usually the main thread) and forces it to wait until the worker thread completes its function. This is the safest way to ensure thread tasks finish before resources are cleared.
* detach(): This separates the thread execution path from the thread object. The thread runs independently in the background, managed directly by the operating system. You cannot join a detached thread later.
* The Assistant Metaphor: Spawning a thread is like hiring an assistant to run an errand for you (like buying groceries) while you stay home. If you lock the front door and go to sleep (let the main function exit) before waiting for the assistant to return (calling join()), the assistant is locked out and the task fails, crashing the system. Detaching is like sending the assistant to deliver flyers. They run on their own schedule, and you do not wait for them.
Modern C++20 Joining Threads: `std::jthread`
Because forgetting to call join() or detach() is a common source of crashes, C++20 introduced std::jthread (joining thread). It acts as a safe wrapper around standard threads:
* Auto Join: In its destructor, std::jthread automatically calls join() when it goes out of scope, preventing terminate crashes.
* Cooperative Cancellation: It supports stop tokens, allowing you to check if a thread has been requested to stop and exit cleanly: