@vczh 轮子哥的回答真的很欢乐哈哈。不过从理解上问题不大,但是和实际情况还是有一些出入的。

unlock()时的release语义实际更像“刷新cache同步到内存”

lock()是的acquire语义更像“从内存同步所有读写操作”

每一次unlock() -> lock() 保证了happen-before的次序(sequence)。

我下面举一个例子

假如是你上面正确的代码(lock()进行acquire, unlock()进行release), 会出现什么情况呢?

A unlock() 时 release, 将自己的写操作“从cache同步到内存”;然后B lock()时 acquire,将“内存中data为1的新值同步到自己cache”。这时B读到的data值为1


那假如如题主所想的不加acquire和release语义,只用release语义呢? A还没来得及刷新自己刚写的新值(因为没有acquire语义强制刷新),B已经读到data的旧值了。这时B读到的data值为0。


看到这里你能体会到 release 后进行 acquire产生了一种happen-before的关系了吗? 一个进程release之前的修改在另一个进程acquire后边的可见了。

ps:

可以看到 “从cache同步到内存” "从内存中的新值同步到cache",我都加了引号。事实情况并不是这样的。举例来说,一些x86架构的CPU有下面途中的结构。


x86可能的一种结构
  • 包括一个store buffer,每次写入操作对store buffer操作,几个周期后同步到本核心的cache
  • 包括一个 invalidate queue,每次其他核心写入新值后,本核心cache不会马上同步,但是会在queue中存入新值得地址处值不再有效。

release (SFENCE)强制刷新store buffer,将改变同步到本地cache;acquire (LFENCE)强制刷新invalidate queue,读入其它核心的改动。值的改动不会经过内存,直接是inter-processor的信息交互。


另外X86除了上面举例的 W-R会乱序外(线程A先读,线程B后读,B可能会读到旧值),其它情况W-W,R-W, R-R在CPU层面都能保证有序(sequential)(要是编译重排就没办法了)。arm、power等其它架构在这种一致性的保证上就要弱很多。



有错误的地方请指正!