Skip to content

Can two atomics keep two values consistent?

Level: 201 · anyone who made each field atomic and called the struct thread-safe

Stub — a question and its expected answers, not yet a lesson. No program behind this page has been written or run, so nothing on it has been through the check that backs every finished page. The table is what the finished lesson is expected to find, and every cell is unverified until a program prints it. (Not machine-checked here.)

One line: A total and a count that are each updated atomically can still be read in a state where one has moved and the other has not, so the average is wrong; an atomic per value makes each value safe and the pair unsafe, and only a lock around both — or a single atomic holding both — keeps an invariant that spans two fields.

The question

A running average keeps total and count. A writer adds a sample: total += x; count += 1, each an atomic add. A reader computes total / count. Between the writer's two adds, the reader sees a total with one more sample than the count — an average that was never true. This page forces that reading with a barrier, as the lost update does, and then shows the three fixes.

What each language is expected to say

Expected answer, to be verified by the program
Rust two AtomicU64s show the bad average; Mutex<(u64, u64)> or one AtomicU128-shaped packing does not
Go two atomic.Int64s, then a sync.Mutex around the pair
C two atomic_longs, then a mutex; or one 64-bit atomic packing two 32-bit fields
C++ std::atomic<Pair> is lock-free only if the pair fits a machine word — is_lock_free() says
Java two AtomicLongs, then synchronized; or an AtomicReference to an immutable record replaced whole
Python the GIL makes each bytecode atomic, not the pair; a Lock around both

What the programs have to show

  • the forced interleaving: writer adds to total, waits at a barrier, reader averages, writer adds to count — the average is off by one sample on every run
  • the same with a lock: never
  • the immutable-snapshot fix, where the pair is replaced as one reference

See also