gpt4 book ai didi

c++ - 了解内存序列和 std::memory_order_relaxed

转载 作者:太空狗 更新时间:2023-10-29 20:20:13 27 4
gpt4 key购买 nike

我正在研究 C++ 内存序列,但它很困惑。

例如:

void sumUp(std::atomic<int>& sum, std::vector<int>& val)
{
int tmpSum = 0;
for(auto i = 0; i < 100; ++i) tmpSum += val[i];

sum.fetch_add(tmpSum, std::memory_order_relaxed);
}

我不明白 sum.fetch_add()tmpSum += val[i] 之后运行。既然顺序不对,sum.fetch_add()可以在tmpSum += val[i]之前操作吗?

那么总和0可能吗?

非常感谢。

最佳答案

memory_order 在单个线程的上下文中没有可观察到的效果:

让我们看看(xab 最初都是0):

auto t1(std::atomic<int>& x, int& a, int& b)
{
a = 3; // 1
b = 5; // 2
x.store(a, std::memory_order_relaxed); // 3
}

因为 (1) 和 (2) 不相互依赖,编译器可以重新排序它们。例如。可以做 (1) -> (2) 或 (2) -> (1)

因为 (3) 取决于 (1)((1) 写入 a 并且 (3) 从 a 读取)编译器不能在 (3) 之前执行 ( 1).这与 (3) 中指定的内存顺序无关

因为 (3) 不依赖于 (2),通常在单线程模型中,编译器可以在 (2) 之前执行 (3)。

但由于 x 是原子的,请考虑另一个线程执行此操作(xab 是对与提供给 t1 的参数相同,并且最初都是 0):

auto t2(std::atomic<int>& x, int& a, int& b)
{
while(x.load(std::memory_order_relaxed) == 3) // 4
assert(b == 5); // 5
}

此线程一直等到 x3,然后断言 b5。现在您可以看到在顺序单线程世界中 (2) 和 (3) 如何在没有任何可观察行为的情况下重新排序,但在多线程模型中,(2) 和 (3) 的顺序可能会对行为产生影响程序。

这就是 memory_order 的作用:它指定可以在原子之前或之后重新排序而对单个线程没有任何影响的操作是否可以这样重新排序。原因是它们可能会对多线程程序产生影响。编译器不知道这一点,只有程序员知道,因此需要额外的 memory_order 参数。

使用 memory_order_relaxed 断言可能会失败,因为 (2) 可能发生在 (3) 之后,但是使用 memory_order_seq_cst(默认)断言永远不会失败,因为 (2) 发生在 (3) 之前。


回到您的示例,无论您指定什么 memory_order,都可以保证 tmpSum += val[i]; 发生在 sum 之前。 fetch_add(tmpSum, std::memory_order_relaxed); 因为第二个依赖于第一个。 memory_order 会影响不影响原子操作的指令的可能重新排序。例如。如果你有一个 int unrelated = 24


顺便说一句,官方术语是“sequenced before”和“sequenced after”


在现实世界中,硬件使事情变得有点复杂。操作可以在当前线程中以一种顺序出现,但另一个线程可以以另一种顺序看到它们,因此更严格的memory_order必须采用额外的措施来确保跨线程的顺序一致.


严格来说,在这个例子中,如果使用 memory_order_relaxed,我们会出现未定义行为,因为对 b 的访问不是跨线程同步的。

关于c++ - 了解内存序列和 std::memory_order_relaxed,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52719079/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com