在同一个互斥锁上的锁定和解锁顺序是不是一致?
Posted
技术标签:
【中文标题】在同一个互斥锁上的锁定和解锁顺序是不是一致?【英文标题】:Are lock and unlock on the same mutex sequential consistent?在同一个互斥锁上的锁定和解锁顺序是否一致? 【发布时间】:2020-06-03 02:44:53 【问题描述】:对于互斥体lock()
,标准的mentions:
同一互斥体上的先前 unlock() 操作与(定义在 std::memory_order 中)此操作同步。
这个answer 试图根据标准解释synchronize-with
的含义。但是,看起来定义没有明确规定。
我的主要问题是,我能得到这个输出吗:
x: 1
y: 2
对于由于线程 A 中的内存重新排序而导致的以下代码?如果在A
解锁后B
锁定,A
中的x
上的写入是否保证被B
观察到?
std::mutex mutex;
int x = 0, y = 0;
int main()
std::thread A[]
x = 1;
std::lock_guard<std::mutex> lg(std::mutex);
y = 0;
;
std::thread B[]
std::lock_guard<std::mutex> lg(std::mutex);
y = x + 2;
;
A.join();
B.join();
std::cout << "x: " << x << std::endl;
std::cout << "y: " << y << std::endl;
如果不是,基于标准的哪一部分? 换句话说,我们可以假设锁定/解锁之间存在顺序一致性吗?
我也看到了这个related 问题,但它是针对单独的互斥锁的。
【问题讨论】:
您的代码具有 UB,因为从x
读取的内容不与对 x
的写入内容独占。因此,我认为无法对内存排序进行推理。一旦允许 UB,标准就不再适用。但是一旦你解决了这个问题,那么事情肯定会被定义,1 2
输出将是不可能的。否则 1.10/10 不会成立。
如果状态 S1 和 S2 之间存在竞争,为什么我们不能推理是否处于状态 S3(即 1 2)?我认为这个问题是:***.com/questions/62164376/…
【参考方案1】:
同步关系是明确定义的。标准规定如下:
某些库调用与另一个线程执行的其他库调用同步。例如,原子存储释放与从存储中获取其值的加载获取同步。 [...] [ 注意: 同步操作的规范定义了何时读取另一个写入的值。对于原子对象,定义很明确。给定互斥体上的所有操作都以单个总顺序发生。每次互斥量获取“读取”上次互斥量释放时写入的值。 ——结束注释 ]
还有:
对原子对象 M 执行释放操作的原子操作 A 与对对象执行获取操作的原子操作 B 同步M 并从版本中的任何副作用中获取其值 以A为首的序列。
换句话说,如果获取操作A“看到”释放操作B存储的值,那么A同步-与B。
考虑一个只需要一个原子布尔标志的自旋锁。所有操作都在该标志上进行。为了获得锁,您已经使用原子读取-修改-写入操作设置了标志。对原子对象的所有修改都完全按照修改顺序进行排序,并且保证 RMW 操作始终读取与该 RMW 操作关联的写入之前写入的最后一个值(按修改顺序)。
由于这种保证,对锁定/解锁操作使用获取/释放语义就足够了,因为成功的锁定操作总是“看到”前一次解锁写入的值。
关于你的问题:
A
中的x
上的写入是否保证被B
观察到如果B
在A
解锁后锁定?
重要的部分是“如果B
在A
解锁之后锁定”!如果可以保证,那么是的,B
的锁定操作与A
的解锁同步,从而建立了先发生关系。因此B
将观察A
的写入。但是,您的代码不能保证B
在A
之后锁定,因此您可能会发生数据竞争,这将导致@ReinstateMonica 正确指出的未定义行为。
更新
对 x 的写入是在A
解锁之前排序的。操作是否在互斥锁之外(之前)都没有关系。事实上,理论上编译器可以重新排序操作,以便它结束在互斥体中(尽管这不太可能)。 Sequenced-before 也是happens-before 定义的一部分,所以我们有以下内容:
std::thread A[]
x = 1; // a
std::lock_guard<std::mutex> lg(std::mutex);
y = 0;
// implicit unlock: b
;
std::thread B[]
std::lock_guard<std::mutex> lg(std::mutex); // c
y = x + 2;
;
假设B
在A
解锁后锁定,我们有:
而且由于happens-before关系是可传递的,因此a发生在c之前。所以是的,对于在A
解锁之前排序的所有操作都是如此 - 无论它们是否在锁内。
【讨论】:
请注意,A 在临界区之前更改了 x。那么我们可以说如果 B 在 A 之后锁定,它会看到 A 所做的所有更改吗? A 在临界区之前和内部都进行了哪些更改? 是的。我已经更新了我的答案,以使这一点更清楚。以上是关于在同一个互斥锁上的锁定和解锁顺序是不是一致?的主要内容,如果未能解决你的问题,请参考以下文章