1. Problem It Solves
Atomicity alone does not specify when writes to other memory become visible. The C++ memory model defines ordering relations, and release/acquire operations can publish ordinary data safely from one thread to another.
Focus on the smallest useful form, its observable behavior, and its safety boundary.
2. Prerequisites
Days 7 and 41-43: threads, atomics, CAS, sequencing, happens-before, and shared non-atomic data.
3. Core Idea
The producer writes data, then performs a release store to a flag. A consumer that reads that value with acquire forms a synchronizes-with edge, making earlier producer writes happen-before later consumer reads.
Identify the objects and types, today's operation, and the printed result. This connects syntax to behavior.
4. Minimal Syntax
data = 42;
ready.store(true, std::memory_order_release);
while (!ready.load(std::memory_order_acquire)) { }
use(data);5. How It Works
The producer writes the ordinary integer before publishing true to the atomic readiness flag.
The consumer loops on an acquire load until it observes the value written by the release store.
The release/acquire pair orders the ordinary data access, so the consumer safely prints 42.
6. Common Mistakes
Replacing both operations with relaxed ordering while still using the flag to publish ordinary data removes the required synchronization.
Do not copy the pattern without checking which atomic value is observed, release sequence, acquire operation, non-atomic data covered, and whether weaker ordering is proven. A program may compile while still having the wrong lifetime, ownership, invalidation, ordering, or performance behavior.
7. When to Use It
Use it when a documented message-passing relation needs lower-level atomic ordering and the invariant is small enough to prove.
Avoid it when a mutex, future, queue, or condition variable expresses the synchronization more safely.
8. Simple Example
One producer publishes a fixed integer through a release flag. One consumer acquires that flag before reading, then the main thread joins both workers.
The .cpp file uses fixed data. Predict its output, compile it, then change one value and test the prediction.
Complete sample code
Source file
cpp14/44_cpp_memory_model_memory_ordering/main.cpp
#include <atomic>
#include <iostream>
#include <thread>
int main() {
int data = 0;
std::atomic<bool> ready{false};
std::thread producer([&] {
data = 42;
ready.store(true, std::memory_order_release);
});
std::thread consumer([&] {
while (!ready.load(std::memory_order_acquire)) {
std::this_thread::yield();
}
std::cout << "data: " << data << "\n";
});
producer.join();
consumer.join();
}
9. Key Takeaways
Memory order is about visibility and ordering between operations, not just indivisible atomic values.
The producer writes data, then performs a release store to a flag. A consumer that reads that value with acquire forms a synchronizes-with edge, making earlier producer writes happen-before later consumer reads.
The compiler or library follows a precise rule; verify which atomic value is observed, release sequence, acquire operation, non-atomic data covered, and whether weaker ordering is proven.
Prefer the smallest form that communicates intent and measure costs when performance matters.
10. Self-Check Questions
Easy — What is the main purpose of The C++ Memory Model and Memory Ordering?
Medium — Which operation makes the producer's earlier write to
datavisible to the consumer in this pattern?Hard — Why must the acquire load observe the release sequence's published value for the synchronizes-with relation to apply?