最小概念了解:一对“发布(publish)/订阅(consume)”规则
Release(释放 / 发布)是什么
发生在写端。
语义:
Release 之前的所有普通读写,在“对外可见的顺序”上,必须发生在 Release 之后的那次写之前。
工程效果:
A 在做完一堆初始化/写入后,执行一次release-store(比如写一个“ready=true”这种发布标志)。
这保证:别的线程如果通过某种同步方式“接住”了这个标志,就不会看到“ready 已经 true,但 data 还是旧值”的那种乱序可见性(至少在 acquire/release 覆盖范围内)。
硬件直觉:
Release 会阻止本核把“发布标志”这次写跑到前面的写之前被其他核观察到。
但它不等价于全栈 flush,也不是“把所有缓存强行写回内存”的那种粗暴理解;它是顺序约束 + 可见性承诺,由架构实现。
Acquire(获取 / 订阅)是什么
发生在读端。
语义(只记这一句):
Acquire 之后的所有普通读写,在本线程的执行顺序上,不能被重排到 Acquire 之前。
工程效果:
B 先做一次acquire-load(比如读 ready)。
如果读到了 ready=true(并且这次 true 来自 A 的 release-store),那么 B 后面读到的 data/startTime/对象字段等,必须至少包含 A 在 release 之前完成的写。
硬件直觉:
Acquire 会阻止本核把后续的 load/store偷跑到“读到发布标志”之前。
你可以把它理解成:拿到“通行证”之前,后面的读取不能提前开始。
Acquire/Release 如何“建立 happens-before”
HB 不是靠“有屏障”这三个字,而是靠配对:
Thread A:
(普通写… ) ; release-store(flag = 1)Thread B:
acquire-load(flag) == 1 ; (普通读…)
如果 B 的 acquire-load 读到的flag==1确实来自A 的 release-store(同一个原子/同步变量上的那次写),则:
A 在 release 之前的所有写 happens-before B 在 acquire 之后的所有读写
和 StoreLoad“全屏障”的关系(避免混淆)
StoreLoad 屏障最强:禁止“前面的写”跨到“后面的读”之后(很多架构里很贵)。
release/acquire 通常更弱、更精确:只保证“发布前的写”不会被观察到落在“发布标志写”之后,以及“订阅后的读写”不会跑到订阅之前。
所以更常用 acquire/release 来做发布订阅;只有在需要更强全局顺序时才用更强序(比如 seq_cst)。