The synchronizes-with relation establishes an ordering between operations in different threads. Its practical value is making earlier writes visible to a thread that observes a synchronization event. Combined with each thread’s own operation order, it creates a transitive happens-before path that can make access to non-atomic data safe. section 5.3
Publish data with an atomic flag
A writer appends 42 to a non-atomic vector, then sets an atomic readiness flag. A reader waits until it loads true before reading the vector. With the default atomic ordering, the flag store synchronizes with the load that reads true. The vector write precedes the store, and the vector read follows the load, so the write happens before the read. Loading false during an earlier loop iteration does not establish that path. section 5.3
Listing 5.2. Reading and writing variables from different threads
#include <vector>
#include <atomic>
#include <iostream>
std::vector<int> data;
std::atomic<bool> data_ready(false);
void reader_thread()
{
while(!data_ready.load()) #1
{
std::this_thread::sleep(std::chrono::milliseconds(1));
}
std::cout<<"The answer="<<data[0]<<"\n"; #2
}
void writer_thread()
{
data.push_back(42); #3
data_ready=true; #4
}
!@%STYLE%@!
{"css":"{\"css\": \"font-weight: bold;\"}","target":"[[{\"line\":7,\"ch\":48},{\"line\":7,\"ch\":50}],[{\"line\":11,\"ch\":48},{\"line\":11,\"ch\":50}],[{\"line\":15,\"ch\":48},{\"line\":15,\"ch\":50}],[{\"line\":16,\"ch\":48},{\"line\":16,\"ch\":50}]]"}
!@%STYLE%@!
Follow the four marked operations: vector write, flag store, flag load that reads true, then vector read. The atomic flag orders the two non-atomic accesses. section 5.3
The basic atomic case pairs a suitably ordered write with a read of the same atomic variable that observes its value. Ordering choices matter: a sequentially consistent store and observing load synchronize; a release store and observing acquire load can do so without imposing a total order on all atomic operations. Relaxed operations alone do not supply that cross-thread ordering. A consume load instead has a dependency-ordering relationship with an appropriate release sequence; do not treat it as an ordinary acquire load. section 5.3.1 section 5.3.3
The acquiring read need not see the release store directly. If successive atomic read-modify-write operations form a release sequence, an acquire load of a value in that sequence can synchronize with its initial release store. For example, consumers claiming items with acquire fetch_sub operations can each observe a queue buffer published before a release store to its item count—even when a later consumer reads a count written by an earlier consumer. section 5.3.4
Listing 5.11. Reading values from a queue with atomic operations
#include <atomic>
#include <thread>
std::vector<int> queue_data;
std::atomic<int> count;
void populate_queue()
{
unsigned const number_of_items=20;
queue_data.clear();
for(unsigned i=0;i<number_of_items;++i)
{
queue_data.push_back(i);
}
count.store(number_of_items,std::memory_order_release); #1
}
void consume_queue_items()
{
while(true)
{
int item_index;
if((item_index=count.fetch_sub(1,std::memory_order_acquire))<=0) #2
{
wait_for_more_items(); #3
continue;
}
process(queue_data[item_index-1]); #4
}
}
int main()
{
std::thread a(populate_queue);
std::thread b(consume_queue_items);
std::thread c(consume_queue_items);
a.join();
b.join();
c.join();
}
The queue listing shows why the second consumer needs the release-sequence rule: its fetch_sub reads the first consumer’s update rather than the producer’s store. section 5.3.4
The same reasoning applies beyond an explicit flag. A mutex unlock can synchronize with a later lock on that mutex, ordering protected writes before the next holder’s accesses. Thread start and successful join also provide specified synchronization points. In a lock-free stack, a release update of the head and an acquiring compare-exchange can order node initialization before pop reads it; an unrelated relaxed load cannot replace that acquire. Release and acquire fences can also establish synchronization through appropriately placed atomic operations. section 5.3.7 section 7.2.5 section 5.3.5


