Acquire-release内存模型用于两个连续的原子操作。

huangapple go评论52阅读模式
英文:

Acquire-release memory model for two consecutive atomic operations

问题

以下代码将写入原子变量A和B,并在另一个线程中以相反的顺序读取它们:

// Initially.
std::atomic<int> A = std::atomic<int>(0);
std::atomic<int> B = std::atomic<int>(0);

// Thread 1
void write()
{
    A.store(1, std::memory_order_release); // line 1
    B.store(1, std::memory_order_release); // line 2
}

// Thread 2
void read()
{
    int valB = B.load(std::memory_order_acquire); // line 3
    int valA = A.load(std::memory_order_acquire); // line 4
}

int main()
{
    std::thread t1(write), t2(read);
    t1.join();
    t2.join();
    return 0;
}

理想情况下,由于在原子B上的释放-获取顺序,行3上的加载应该同步完成行1上的存储,因为它恰好在行2上的释放操作之前发生。但是参考仅提到(强调是我的) -

所有内存写操作 (非原子和松散原子) 都发生在线程A的角度上的原子存储之前,成为线程B中可见的副作用。

因此,B上的释放-获取排序不考虑其他不是松散内存排序的原子操作吗?

那么,在Thread 2中完成read()后,是否有可能出现valB = 1valA = 0的值,因为B的内存排序不关心其他不是松散的原子操作?这里需要顺序一致性吗?


<details>
<summary>英文:</summary>

Following code writes into atomic A, B variable and reads them in opposite order in another thread:

```cpp
#include &lt;atomic&gt;
#include &lt;thread&gt;

// Initially.
std::atomic&lt;int&gt; A = std::atomic&lt;int&gt;(0);
std::atomic&lt;int&gt; B = std::atomic&lt;int&gt;(0);

// Thread 1
void write()
{
    A.store(1, std::memory_order_release); // line 1
    B.store(1, std::memory_order_release); // line 2
}

// Thread 2
void read()
{
    int valB = B.load(std::memory_order_acquire); // line 3
    int valA = A.load(std::memory_order_acquire); // line 4
}

int main()
{
    std::thread t1(write), t2(read);
    t1.join();
    t2.join();
    return 0;
}

Ideally, given the release-acquire ordering on atomic B, the load on line 3, should synchronize for completion of store on line 1, since it happens to be before release operation on line 2. But the reference only mentions (emphasis, mine) -

> All memory writes (non-atomic and relaxed atomic) that happened-before the atomic store from the point of view of thread A, become visible side-effects in thread B.

So, a release-acquire ordering on B, doesn't take into effect the atomic oparations on other variables that aren't relaxed memory ordering?

Is it possible to have then, values for valB = 1, valA = 0, after read() concludes in Thread 2, since B's memory ordering doesn't care about other atomic operations that aren't relaxed? Do we need sequential consistency here?

答案1

得分: 4

不可能。正如您引用的那样:

"所有内存写入(非原子和松散原子)在线程A的视图中发生在原子存储之前,都会成为线程B中可见的副作用。"

因此,当您从B中读取1时,从A发生在写入线程中是可见的,因此随后从A中的读取也必须返回1。

您强调的括号中的部分可能更好地用"(包括非原子和松散原子)"来表述 - 这是让您困惑的吗?

标准是怎么说的?

标准的相关部分将是atomics.order.1.4:
"执行释放操作的原子操作A与执行对M上的获取操作的原子操作B同步"

"synchronizes with"的含义在intro.multithread中有描述:
"如果评估A与评估B发生在不同线程之间,且A与B同步,则A在不同线程之间发生在B之前* [...]

评估AB之前*(或等效地,B发生在A之后),如果:

  • A在B之前序列化,或
  • A在不同线程之间发生在B之前。

Happens-before是可传递的:A.store()在A.load()之前发生(在序列化之前发生),在B.load()之前发生(与之同步),在A.load()之前发生(在序列化之前发生)。因此,A.store()在A.load()之前发生。

标准明确规定了原子对象:

如果对原子对象M的副作用X对M的值计算B之前发生,则评估B必须从X或M的修改顺序中跟随X的副作用Y中获取其值。

因此,由于A.store(1)在A.load()之前发生,并且没有其他候选操作Y,A.load()必须返回1。

我还可以推荐这篇博客文章。

英文:

> Is it possible to have then, values for valB = 1, valA = 0, after read() concludes in Thread 2, since B's memory ordering doesn't care about other atomic operations that aren't relaxed?

No that's not possible. As you quoted:

> All memory writes (non-atomic and relaxed atomic) that happened-before the atomic store from the point of view of thread A, become visible side-effects in thread B.

So when you read 1 from B, the happened-before write to A is visible in the reading thread and thus the following read from A must also return 1.

The part in parentheses that you highlighted might be better worded as "(including non-atomic and relaxed atomic)" – is that what tripped you up?

What does the standard say?

The relevant part of the standard would be atomics.order.1.4:
> An atomic operation A that performs a release operation on an atomic object M synchronizes with an atomic operation B that performs an acquire operation on M

The meaning of "synchronizes with" is described in intro.multithread:

> An evaluation A inter-thread happens before an evaluation B if A synchronizes with B [...]

> An evaluation A happens before an evaluation B (or, equivalently, B happens after A) if
> * A is sequenced before B, or
> * A inter-thread happens before B.

Happens-before is transitive: A.store() happens-before (sequenced before) B.store(), happens-before (synchronizes-with) B.load(), happens-before (sequenced before) A.load(). Thus A.store() happens-before A.load().

The standard explicitly states about atomic objects:

> If a side effect X on an atomic object M happens before a value computation B of M, then the evaluation B shall take its value from X or from a side effect Y that follows X in the modification order of M.

So, since A.store(1) happens-before A.load() and there are no other candidate operations Y, A.load() must return 1.

I can also recommend this blog post.

huangapple
  • 本文由 发表于 2023年2月6日 19:34:42
  • 转载请务必保留本文链接:https://go.coder-hub.com/75360789.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定