Memory Models and Atomic Ordering — acquire/release/seq_cst TODO
Concept
As long as single-thread semantics are preserved, CPUs and compilers are free to reorder memory accesses, so what other threads see, and in what order, is governed by the memory model. A relaxed atomic operation only guarantees the atomicity of the operation itself, not its ordering relative to surrounding accesses. When a release store pairs with an acquire load that reads its value, a happens-before relationship is established: everything written before the release becomes visible to code after the acquire. seq_cst goes further and guarantees a single global order across all seq_cst operations, making it the most expensive. Go doesn't expose these ordering options directly — it describes its memory model in terms of the happens-before rules created by channels, mutexes, and sync/atomic.
Getting ordering wrong usually still passes on x86, and only shows up under a weaker memory model like ARM or under heavy load, which makes it an extremely hard-to-reproduce bug.
Code & Formula
# 메모리 모델과 원자성 순서 — release/acquire 페어링이 만드는 happens-before 관계를 흉내낸다.
# (Python 은 GIL 때문에 진짜 하드웨어 재배열은 안 보이지만, release-store -> acquire-load 짝짓기 패턴 자체는 동일하다.)
import threading
data = 0
ready = threading.Event() # release/acquire 짝을 흉내내는 신호: set()=release, wait()=acquire
observations = []
def writer():
global data
data = 42 # release 이전의 모든 쓰기는...
ready.set() # ...release. 이 시점 이후 acquire 한 쪽에는 반드시 42가 보여야 한다.
def reader():
ready.wait() # acquire: release 이전의 모든 쓰기가 happens-before 로 보장되어 보인다.
observations.append(data) # 만약 순서 보장이 없었다면(relaxed) 0을 볼 수도 있었다.
# seq_cst 라면 여기에 더해 "모든 스레드가 동의하는 단일 전역 순서"까지 보장하지만, 비용이 가장 크다.
runs_correct = 0
for _ in range(1000):
data = 0
ready.clear()
t_r = threading.Thread(target=reader)
t_w = threading.Thread(target=writer)
t_r.start(); t_w.start()
t_r.join(); t_w.join()
if observations[-1] == 42:
runs_correct += 1
print("total runs:", len(observations))
print("runs where acquire correctly saw release's write:", runs_correct)
print("release/acquire happens-before held every time:", runs_correct == len(observations))
docs/code/algorithms/algorithms-36.py
Exercise
Implement the classic flag/data variable pattern once with relaxed and once with release/acquire ordering, run it millions of times on an ARM-family device, and count how often the ordering appears reversed.
Practical Connection
This is the correctness basis for lock-free queues and shared-state flags in high-performance server code — for Verex's in-memory order book shared across multiple goroutines, it's the standard for judging where atomic operations suffice and where a mutex becomes necessary.
If you study this on a given day, add a note link and a ✅ to this line in the source curriculum (docs/knowledge/dev-100-curriculum.md) and this spot will lead straight to the note body. You can also write directly on this page — but regenerating overwrites it, so it's safer to keep anything you want to save as markdown under docs/algorithms/.