liveBook · Manning

Sequential consistency

As explained in C++ Concurrency in Action, Second Edition

Sequentially consistent ordering requires atomic operations using that ordering to fit one total order consistent with each thread's program order.

Sequential consistency gives atomic operations using `std::memory_order_seq_cst` one total order consistent with each thread’s program order. It is the default ordering for C++ atomic operations and is generally easier to reason about than weaker orderings. section 5.3.3

Two flags, two readers

One thread sets `x`; another sets `y`. A reader waits for `x`, then checks `y`; a second waits for `y`, then checks `x`. With sequentially consistent operations, both final checks cannot be false: that would require each flag’s store to precede one reader’s observation but follow the other’s, forming a cycle in the total order. The assertion that at least one check succeeds therefore holds. section 5.3.3

Listing 5.4. Sequential consistency implies a total ordering
#include <atomic>
#include <thread>
#include <assert.h>
std::atomic<bool> x,y;
std::atomic<int> z;
void write_x()
{
    x.store(true,std::memory_order_seq_cst);  #1
}
void write_y()
{
    y.store(true,std::memory_order_seq_cst);  #2
}
void read_x_then_y()
{
    while(!x.load(std::memory_order_seq_cst));
    if(y.load(std::memory_order_seq_cst))     #3
        ++z;
}
void read_y_then_x()
{
    while(!y.load(std::memory_order_seq_cst));
    if(x.load(std::memory_order_seq_cst))     #4
        ++z;
}
int main()
{
    x=false;
    y=false;
    z=0;
    std::thread a(write_x);
    std::thread b(write_y);
    std::thread c(read_x_then_y);
    std::thread d(read_y_then_x);
    a.join();
    b.join();
    c.join();
    d.join();
    assert(z.load()!=0);                      #5
}


!@%STYLE%@!
{"css":"{\"css\": \"font-weight: bold;\"}","target":"[[{\"line\":7,\"ch\":46},{\"line\":7,\"ch\":48}],[{\"line\":11,\"ch\":46},{\"line\":11,\"ch\":48}],[{\"line\":16,\"ch\":46},{\"line\":16,\"ch\":48}],[{\"line\":22,\"ch\":46},{\"line\":22,\"ch\":48}],[{\"line\":38,\"ch\":46},{\"line\":38,\"ch\":48}]]"}
!@%STYLE%@!

Follow the stores and loads in this four-thread example to test whether both readers could miss the other flag. section 5.3.3

Acquire-release and relaxed ordering offer different guarantees. Sequential consistency may require additional synchronization instructions on some architectures, so weaker ordering can matter for performance—but it also makes correctness harder to establish. section 5.3.3

How C++ Concurrency in Action, Second Edition covers it

How deep it goes
Works through a four-thread total-order argument and compares guarantees and costs, without detailing CPU instruction sequences.
Examples
Uses two atomic flags, two waiting readers, and an assertion that at least one reader sees both flags set.
What you can do after
Evaluate/understand whether sequentially consistent atomic operations rule out a proposed four-thread observation.
How it connects
Connects atomic operations and program order with acquire-release ordering, relaxed ordering, and happens-before.

Also explained in

Compare how 2 books explain sequential consistency →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of C++ Concurrency in Action, Second Edition. The section links point to the parts of the book it draws on.