Threads: why and how
What you'll learn:
- start a function in a separate thread and wait for it with
join(); - explain what a data race is and protect shared data with a mutex;
- choose between a mutex and
std::atomic; - collect a result from another thread with
std::asyncand measure time with the types from<chrono>.

Picture a restaurant kitchen. A cook makes soup from a recipe card: step by step, one line at a time. While the cook chops vegetables, nobody makes the salad, even though there is a free spot at the counter. A second cook could stand there and make the salad at the same time as the soup. Each cook has their own card, but they share one fridge.
A program works the same way. It executes its statements one at a time: while a function computes, the rest of the work waits. Yet a processor usually has several cores, which are the spots at the counter. Each core executes its own sequence of statements at the same time as the others.
A thread is a cook with their own card: a sequence of statements that executes independently of the others. Every program has at least one thread, the one that runs main. To run two tasks at once, you need a second thread. The std::thread object from the <thread> header starts it:
#include <iostream>
#include <thread>
void print_squares() {
for (int i = 1; i <= 3; ++i) {
std::cout << i * i << ' '; // 1 4 9
}
}
int main() {
std::thread worker(print_squares);
worker.join();
std::cout << "done\n"; // done
}This is where the counter-spot analogy ends: you can start more threads than there are cores. A core then switches between threads quickly, and from the outside their work still looks simultaneous.
Starting and waiting
Let's go through main line by line:
std::thread worker(print_squares);creates a thread and immediately startsprint_squaresin it. Meanwhile,maindoes not wait: the next line runs in the main thread alongside the new one.worker.join();stopsmainuntil theworkerthread finishes. This is the head cook waiting for the second one to finish the salad.std::cout << "done\n";runs only after that, sodoneis always printed after the squares.
If main printed done before join(), the output of the two threads could mix. The order in which threads execute their statements is not known in advance.
Arguments and shared memory
The function's arguments go to the constructor after its name: std::thread t(f, 1, 2) calls f(1, 2) in the new thread. You can pass a lambda instead of a function, too.
All threads of a program share its memory, like the cooks share the fridge: a global variable is one and the same for all of them.
The course's exercises have no threads: code with std::thread or std::async does not work there, so run this chapter's examples on your own computer.
detach and jthread
Instead of join(), you can call detach(): the thread keeps running on its own, but you can no longer wait for it. That is risky. If such a thread reads local variables of the function that started it, it may access them after they are destroyed. That is undefined behavior.
C++20 added std::jthread. It is started the same way as std::thread, but the std::jthread destructor calls join() by itself. So you cannot forget to wait for such a thread.
Common mistake
The thread is started but never waited for:
int main() {
std::thread worker(print_squares);
std::cout << "done\n";
} // ✗terminate called without an active exceptionThe program builds without warnings. But on exit from main, the worker object is destroyed although neither join() nor detach() has been called on it. In that case the std::thread destructor calls std::terminate, and the program ends abnormally. Call join() before the end of the scope, or use std::jthread.
Key points
- A thread is a sequence of statements that runs at the same time as others;
std::threadstarts a function in a new thread. join()waits until the thread finishes; the order of different threads' statements is not known in advance.- All threads of a program share one memory.
- A
std::threadobject destroyed withoutjoin()ordetach()callsstd::terminate;std::jthreadcallsjoin()by itself.
Subscribe to take the course
The course is free. Sign in with Telegram and subscribe to the channel to open every lesson.