1. Problem It Solves
Concurrent threads may access shared state at the same time, so the program needs defined rules for visibility and ordering. It makes an important assumption visible and checkable.
2. Prerequisites
Functions, lambdas, object lifetime, and RAII.
You should be able to compile a short program and read its output.
3. Core Idea
Imagine each thread has its own desk. An atomic operation is a synchronized mailbox, and the memory model defines which writes another desk is guaranteed to see. Read std::atomic as a precise promise; runtime preconditions still belong to the programmer.
4. Minimal Syntax
std::atomic<int> counter{0};
counter.fetch_add(1);5. How It Works
The program introduces the smallest relevant form of
std::atomic.It applies the feature to fixed data while required owners remain in scope.
It prints one result that can be checked against the source.
6. Common Mistakes
Unsynchronized conflicting access to a non-atomic object is a data race and therefore undefined behavior, not merely an occasionally wrong count.
Also check the required header, C++20 library support, lifetime, and deduced types.
7. When to Use It
Use it when multiple threads share a small independent value such as a counter or flag.
Avoid it when several fields form one invariant that is clearer under a mutex.
8. Simple Example
Two threads increment one atomic counter and joining them creates a clear completion point before printing. The companion .cpp uses no input, so its result is easy to reproduce.
Complete sample code
Source file
cpp20/06_thread_atomic_memory_model/main.cpp
// Day 6: Threads, Atomics, and the C++ Memory Model
#include <atomic>
#include <iostream>
#include <thread>
int main() {
std::atomic<int> counter{0};
auto work = [&counter] {
for (int i = 0; i < 1000; ++i) {
counter.fetch_add(1);
}
};
std::thread first{work};
std::thread second{work};
first.join();
second.join();
std::cout << "counter = " << counter.load() << '\n';
}
9. Key Takeaways
std::atomicexpresses the central C++20 idea of this day.The example isolates one behavior with fixed data.
Compiler checks do not replace lifetime and runtime reasoning.
Prefer the smallest interface that states the real requirement.
10. Self-Check Questions
Easy — What is the main job of
std::atomicin the minimal example?Medium — Why is the final value deterministic after both
joincalls even though the increment order is not?Hard — Would replacing the atomic with
volatile intremove the data race? Explain using visibility and atomicity rather than timing.