在 C++ 标准中滥用 std::memory_order::relaxed 的示例 [algorithms.parallel.exec/5 in n4713]

Posted

技术标签:

【中文标题】在 C++ 标准中滥用 std::memory_order::relaxed 的示例 [algorithms.parallel.exec/5 in n4713]【英文标题】:Example of misuse of std::memory_order::relaxed in C++ Standard [algorithms.parallel.exec/5 in n4713] 【发布时间】:2019-10-08 14:02:59 【问题描述】:

在 C++ 标准中滥用std::memory_order::relaxed 的例子之一:

std::atomic<int> x0;
int a[] = 1,2;
std::for_each(std::execution::par, std::begin(a), std::end(a), [&](int) 
    x.fetch_add(1, std::memory_order::relaxed);
    // spin wait for another iteration to change the value of x
    while (x.load(std::memory_order::relaxed) == 1)   // incorrect: assumes execution order
);

然后它说,

上面的例子取决于迭代的执行顺序,如果两个迭代在同一个执行线程上顺序执行,则不会终止。

问题:

    评论说,“不正确:假定执行顺序”。什么是“假定的执行顺序”?我想念它。

    “上述示例取决于迭代的执行顺序”中的“迭代”指的是什么?这是否意味着while循环中的迭代?或者它是指 std::for_each的迭代?

    如果std::for_each 的迭代由不同的线程并行执行,那么其中一个迭代/线程不会退出是否仍然正确?因为x.fetch_add(1, std::memory_order::relaxed) 是原子的,所以一个线程将使x 1 而另一个线程将使x 2 并且不可能有两个线程的x == 1。没有?

【问题讨论】:

Re 3.:我认为你误读了while。它会阻塞 while x == 1,而不是 直到。如果两个线程最终都读取x == 2(如果原子没有放松,它们会读取),那么一切都会好起来的。 @MaxLanghof 啊,是的,我的错。谢谢! 请在引用标准时包含章节参考(来自 [algorithms.parallel.exec/3])。此外,这与滥用 std::memory_order_relaxed 无关——我的意思是它甚至会发生在 std::memory_order_seq_cst 上,但是关于并行执行的错误假设 std::for_each,正如 Maxim Egorushkin 解释的那样。 @MaxLanghof 什么会改变“宽松”的保证? @curiousguy 你是对的,我的评论是错误的。 【参考方案1】:

“不正确:假定执行顺序”。 “假定的执行顺序”是什么?

它假定 lambda 的主体由多个线程而不是一个线程执行。标准宁愿说它可以并行执行。

“上面的例子取决于迭代的执行顺序”中的“迭代”指的是什么?

它可能是指另一个线程执行 lambda。但标准不保证有另一个线程。见execution_policy_tag_t:

parallel_policy 执行策略类型用作唯一类型以消除并行算法重载的歧义并指示并行算法的执行可以并行化。使用此策略调用的并行算法(通常指定为 std::execution::par)中元素访问函数的调用被允许在调用线程或库隐式创建的线程中执行以支持并行算法执行。在同一线程中执行的任何此类调用相对于彼此的顺序是不确定的。

【讨论】:

以上是关于在 C++ 标准中滥用 std::memory_order::relaxed 的示例 [algorithms.parallel.exec/5 in n4713]的主要内容,如果未能解决你的问题,请参考以下文章

如果我必须分解 REST 数据,然后重新聚合它,我是不是在滥用 GraphQL?

通过Chrome滥用即时支付应用

C#的dynamic使用中有啥需要注意的地方,以免滥用

十类C++标准库 十类C++标准库简介

增加md5输出的长度

sizeof(bool) 是不是在 C++ 语言标准中定义?