C++
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Object-Oriented C++
-
Section 4: Memory Management
-
Section 5: Templates and Generic Programming
-
Section 6: The Standard Template Library
-
Section 7: Modern C++ Features
-
Section 8: Error Handling
-
Section 9: Multithreading
-
Section 10: Operator Overloading and Type Conversion
-
Section 11: Advanced Topics
-
Section 12: Tooling and Build Systems
-
Section 13: Design Patterns in C++
-
Section 14: Interfacing with C and Systems Programming
-
Section 15: Networking and IPC Basics
-
Section 16: Graphics and Game Programming Basics
-
Section 17: The Boost Libraries
-
Section 18: Data Structures and Algorithms in C++
-
Section 19: Practical Projects
-
Section 20: More Concurrency Patterns
-
Section 21: More OOP and Design Practice
-
Section 22: File I/O and Streams
-
Section 23: More Standard Library
-
Section 24: Practice Exercises
-
Section 25: Interview and Algorithm Practice
-
Section 26: Compiler and Language Internals
-
Section 27: GUI and Application Frameworks Overview
-
Section 28: Testing and Quality Practices
-
Section 29: Numerics Library
-
Section 30: Concepts Library (C++20)
-
Section 31: Ranges Library (C++20) In Depth
-
Section 32: More Utility Library
106: Joining and Detaching Threads
Think of starting a thread like hiring a freelance contractor to do a specific job for you. Once you've handed them the instructions and the tools, you have a choice in how you handle the end of that relationship. You can either wait at the door until they finish the job and hand you the keys back (that's joining), or you can tell them, "Just finish whenever you're done, I don't need to know when it's over" (that's detaching).
In C++, if you hire a contractor (create a std::thread object) and then just walk away or "fire" the object (let it go out of scope) without explicitly choosing one of those two options, C++ doesn't just let it slide. It assumes you've made a catastrophic logic error and will call std::terminate(), crashing your entire program. I've seen plenty of juniors spend hours debugging a crash only to realize they forgot to call .join().
Waiting for the Work to Finish
When you call .join(), the calling thread (usually your main thread) stops dead in its tracks. It won't execute another line of code until the thread you joined has completely finished its execution. This is your synchronization point.
#include <iostream>
#include <thread>
#include <vector>
#include <numeric>
void sum_large_array(const std::vector<int>& data, long long& result) {
result = std::accumulate(data.begin(), data.end(), 0LL);
}
int main() {
std::vector<int> numbers(1000000, 1);
long long total = 0;
// We start the thread to do the heavy lifting
std::thread worker(sum_large_array, std::ref(numbers), std::ref(total));
// If we tried to print 'total' right here, it would likely be 0
// because the worker is still running in the background.
worker.join(); // We wait here. The main thread pauses.
std::cout < "The total is: " < total < std::endl;
return 0;
}
In this case, join() is non-negotiable. We can't print the result until the calculation is actually done. By joining, we ensure the worker thread has finished its work and its resources are cleaned up before we move on.
Setting it Free (and the Risks Involved)
Sometimes, you have a task that is "fire and forget." Maybe it's a background logger that writes telemetry to a file, or a heartbeat signal that pings a server every ten seconds. You don't want your main application to freeze just to wait for a log entry to be written to a slow disk.
That's where .detach() comes in. It severs the connection between the std::thread object and the actual OS thread. The thread keeps running in the background, but you no longer have a handle to control it or wait for it.
#include <iostream>
#include <thread>
#include <chrono>
void background_logger() {
while (true) {
std::cout < "[Log]: System healthy... " < std::endl;
std::this_thread::sleep_for(std::chrono::seconds(2));
}
}
int main() {
std::thread logger(background_logger);
logger.detach(); // We tell the OS: "Just let this run on its own."
std::cout < "Main application is running independently!" < std::endl;
std::this_thread::sleep_for(std::chrono::seconds(5));
return 0; // When main exits, the detached thread is killed abruptly.
}
Now, a word of caution from someone who has spent too many nights chasing memory corruption: be incredibly careful with detached threads. Because the thread is running independently, it might try to access a variable or an object that the main thread has already destroyed. If background_logger tried to access a local variable from main() after main() had returned, you'd have a dangling reference and a very nasty crash.
Deciding Which Path to Take
I generally advise leaning toward join(). It's safer, more predictable, and makes the lifetime of your data much easier to reason about. Use detach() only when the task is truly independent and doesn't rely on any resources that could possibly be destroyed before the thread finishes.
Just remember: every std::thread you instantiate must end in either a join() or a detach() before the object is destroyed. No exceptions.
📋 Practical Task
Exercise: The Asynchronous File-Writer and Processor
You are building a simplified data processing pipeline. You need to implement a program that does two things:
- Processes a "heavy" dataset (represented by a sleep timer) and provides a result that the main program must use.
- Runs a background "Heartbeat" monitor that prints a status message every second, which the main program should not wait for.
Requirements:
- Create a function
void processData(int& result)that sleeps for 2 seconds and then sets the result to 42. - Create a function
void heartbeat()that prints "Heartbeat active..." in a loop every 1 second. - In
main(), start both threads. - Correctly use
join()anddetach()so that the program prints the result ofprocessData, but allows theheartbeatto run in the background without blocking the final output. - Ensure the program does not crash upon exit.
There are no comments for now.