$ ./count# total too low, varies200000$ ./count200000mainthread aa's register–1constexpr int N = 100000;2int hits = 0;std::atomic<int> hits{0};3void work() {void work() { int local = 0;4 for (int i = 0; i < N; ++i)5 ++hits; ++local;6} hits += local; }7int main() {8 std::thread a(work), b(work);9 a.join(); b.join();10 std::cout << hits << '\n';11}hits · memory0thread bb's register–5566+1+1200000200000100000100000!
Violet card: the program (count.cpp); line 2 is the shared counterSand blocks: threads; rose boxes: CPU registers; slate box: hits in memoryTokens: values loaded and stored, atomic +1 adds, ✓ for join(), a red ! for a lost updateOn the left: the program count.cpp. At the back: the terminal and the main thread. On the right: threads a and b, each with the register of its CPU core, and the shared variable hits in memory between them. With a plain int, ++hits is a load, an add and a store; two threads doing it at once can both load 5 and both store 6, so an update is lost: a data race. With std::atomic<int>, each ++hits is one indivisible step, so no update is lost; join() waits for both threads and main prints 200000. The card leaves out the program's first three lines, #include <atomic>, #include <iostream>, #include <thread>. The full program compiles with g++ or clang++ using -std=c++20 -Wall -Wextra and prints 200000.

Threads

  1. A thread is one path through your code: it runs one statement after another, with its own stack. Every program starts with one thread, the main thread, which runs main().

A data race

Atomics

Join and print

Going faster

What happens

;; step 1/11 · one-thread
thread   one path of execution: statement after statement
         each thread has its own stack and its own place in the code
the main thread starts at main() (line 7)
for now, the counter is a plain int (line 2):
  int hits = 0;
the card leaves out three lines at the top: #include <atomic>  #include <iostream>  #include <thread>

The card leaves out the program's first three lines, #include <atomic>, #include <iostream>, #include <thread>. The full program compiles with g++ or clang++ using -std=c++20 -Wall -Wextra and prints 200000. -pthread tells GCC and Clang to use the system's POSIX threads library. Recent Linux and macOS systems often build without it, but older Linux systems need it, so it is safest to always add it. Microsoft's compiler needs no extra flag.