acquire/release 语义真的足以实现关键部分吗?

Are acquire/release semantics really enough for implementing critical sections?

(这里,临界区,我的意思是任何同步机制,防止对某些资源的并发访问。)

似乎网络上的共识是进入临界区时获取语义,离开临界区时释放语义。但是这不是打开了死锁的可能性吗?

这里有一些伪代码来解释我的意思。原代码如下:

Thread 1:
    enter A // acquire semantics
    // ... some work within A
    leave A // release semantics

    enter B // acquire semantics
    // ... some work within B
    leave B // release semantics

Thread 2:
    enter B // acquire semantics
    // ... some work within B
    leave B // release semantics

    enter A // acquire semantics
    // ... some work within A
    leave A // release semantics

执行这段代码时,CPU 可以合法地将其转换成这样(获取前没有移动,发布后没有移动):

Thread 1:
    enter A // acquire semantics
    enter B // acquire semantics
    // ... some work within A
    // ... some work within B
    leave A // release semantics
    leave B // release semantics

Thread 2:
    enter B // acquire semantics
    enter A // acquire semantics
    // ... some work within B
    // ... some work within A
    leave B // release semantics
    leave A // release semantics

但是现在,我们遇到了以前没有的死锁危险!两个线程进入多个临界区,但顺序不同。

那么关键部分不需要防止 store/load 重新排序吗? IE。他们不需要顺序一致的语义而不仅仅是 acquire/release 吗?为什么没有指定

"moves" 对 acquire/release 的解释是一个有用的指南,但可能会失效,因为它描述了线程如何看待 其他线程 的操作。每个线程都必须将自己的操作视为按顺序发生。例如,线程 2 可以看到这个序列:

  1. 线程 1 获取 A
  2. 线程 2 获取 B

但随后线程 2 会将自己视为在获取 A 之前释放 B。相反,在相同的 运行 期间,线程 1 可以将其视为:

  1. 线程 2 获取 B
  2. 线程 1 获取 A

但随后线程 1 会将自己视为在获取 B 之前释放 A。