繁体   English   中英

了解内存序列和std :: memory_order_relaxed

[英]Understanding memory sequences and std::memory_order_relaxed

我正在研究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读取)编译器在(1)之前不能做(3)。 这与(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);之前发生sum.fetch_add(tmpSum, std::memory_order_relaxed); 因为第二个取决于第一个。 memory_order会影响可能重新排序不影响原子操作的指令。 例如,如果你有一个int unrelated = 24


顺便说一句,官方术语是“先前排序”和“后排序”


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


严格来说,在此示例中,如果使用memory_order_relaxed我们将具有未定义的行为,因为对b的访问不会跨线程同步。

总和是0可能?

不,这不对。 std::memory_order_relaxed表示sum并发访问通常不是有序的; 与此同时,在这个特定的线程中, tmpSum的计算在tmpSum 之前被 fetch_add ,因此传递给fetch_add的值与循环中计算的值一致。 因此, fetch_add不保证所有特定线程tmpSum都以什么顺序添加到所有线程中,但这完全没有关系,因为整数加法通勤; 但是语言语义保证了fetch-added的值是每次向量的总和。

由于它不是有序的,sum.fetch_add()可以在tmpSum + = val [i]之前运行吗? 总和是0可能?

没有

无论as-if规则对加载和存储的重新排序如何,程序仍必须按照书面指令的明确顺序执行'as-if'代码。

什么std::memory_order_relaxed意味着:

  • 对于在不同线程中发生的总和的其他原子操作,add将原子地发生。

  • 另一个线程可能不会立即观察到总和的变化,但很快就会在某个时刻观察到。

不,为什么会这样? 语句按顺序执行,这意味着首先是100个添加操作,然后是fetch_add 因为你正在使用原子,我猜你正在做一些多线程的东西。 可能是,如果函数并行执行多次,则单个fetch_add以任意顺序发生。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM