当前位置: 首页 > 知识库问答 >
问题:

原子负载可以在C++内存模型中合并吗?

云宾鸿
2023-03-14

请考虑下面的C++11代码片段。对于GCC和clang,这将编译为两个(顺序一致的)Foo加载。(编者注:编译器不会优化atomics,更多细节请参见本问答,特别是http://wg21.link/n4455标准讨论了这可能产生的问题,该标准没有给程序员提供解决这些问题的工具。本语言律师问答是关于当前标准的,而不是编译器的工作。)

C++内存模型是否允许编译器将这两个加载合并为一个加载,并对x和y使用相同的值?

#include <atomic>
#include <cstdio>

std::atomic<int> foo;

int main(int argc, char **argv)
{
    int x = foo;
    int y = foo;

    printf("%d %d\n", x, y);
    return 0;
}

共有1个答案

岳锦
2023-03-14

允许一个实现将您的代码片段转换为以下内容(伪实现)。

int __loaded_foo = foo;

int x = __loaded_foo;
int y = __loaded_foo;

原因是在保证顺序一致性的情况下,您无法观察到上面两个不同的foo负载之间的差异。

注意:不仅仅是编译器可以进行这样的优化,处理器可以简单地推断,您无法观察到差异并加载foo的值一次--尽管编译器可能要求它加载两次。

给定一个线程以增量方式不断更新foo,可以保证的是,YX的内容相比,Y将具有相同的值,或者更晚的写入值。

// thread 1 - The Writer
while (true) {
  foo += 1;
}
// thread 2 - The Reader
while (true) {
  int x = foo;
  int y = foo;

  assert (y >= x); // will never fire, unless UB (foo has reached max value)
}                  

设想编写线程由于某种原因在每次迭代时暂停执行(因为上下文切换或其他实现定义的原因);您无法证明这就是导致XY具有相同值的原因,或者是因为“合并优化”。

    null

只要我们不能观察到我们所表达的行为和执行过程中的行为之间的任何差异,实现就可以进行它想要的任何更改。

    null
// initial state
std::atomic<int> foo = 0;
// thread 1
while (true) {
  if (foo)
    break;
}
// thread 2
foo = 1

问题:给定前面几节中的推理,一个实现是否可以只在线程1中读取foo,然后即使线程2写入foo,也不会跳出循环

答案;没有。

如果我真的想防止这种形式的“优化”怎么办?

如果您希望强制实现在编写某个变量的每一点上都实际读取该变量的值,那么应该研究volatile的用法(注意,这绝不会增强线程安全)。

但是实际上编译器并不优化atomics,因此标准组建议不要使用volatile Atomic,直到这个问题解决为止。见

  • http://wg21.link/n4455
  • http://wg21.link/p0062
  • 为什么编译器不合并冗余的STD::Atomic写入?
  • 和这个问题的重复,编译器能优化出两个原子负载吗?
 类似资料:
  • 问题内容: 我有几个在单个WebLogic集群中运行的J2EE应用程序实例。 在某些时候,这些应用程序会进行合并以将记录插入或更新到后端Oracle数据库中。MERGE检查是否存在具有指定主键的行。如果在那里,请更新。如果没有,请插入。 现在,假设有两个应用程序实例要插入或更新主键= 100的行。假设该行不存在。在合并的“检查”阶段,他们都看到行不在此处,因此他们都试图插入。然后,我得到了唯一的键

  • 本章主要内容 C++11内存模型详解 标准库提供的原子类型 使用各种原子类型 原子操作实现线程同步功能 C++11标准中,有一个十分重要特性,常被程序员们所忽略。它不是一个新语法特性,也不是新工具,它就是多线程(感知)内存模型。内存模型没有明确的定义基本部件应该如何工作的话,之前介绍的那些工具就无法正常工作。那为什么大多数程序员都没有注意到它呢?当你使用互斥量保护你的数据和条件变量,或者是“期望”

  • 本文向大家介绍Java内存模型原子性原理及实例解析,包括了Java内存模型原子性原理及实例解析的使用技巧和注意事项,需要的朋友参考一下 这篇文章主要介绍了Java内存模型原子性原理及实例解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 本文就具体来讲讲JMM是如何保证共享变量访问的原子性的。 原子性问题 原子性是指:一个或多个操作,要么全部执行

  • 问题内容: 年复一年,我试图了解部分与内存模型和并发交易的Java规范。我不得不承认我失败了。是的,我了解锁和“同步”,wait()和notify()。我可以很好地使用它们,谢谢。对于“ volatile”的作用,我什至不清楚。但是所有这些都不是来自语言规范,而是来自一般经验。 这是我要问的两个示例问题。我对特定答案不太感兴趣,因为我需要了解答案是如何从规范中得出的(或者可能是我得出结论,规范没有

  • 我知道下一个场景:(奇怪的格式,我知道) 如果线程#1和线程#2在完全相同的时间输入,这将发生: > 两者都将执行" CMPXCHG指令同时对两个线程生效: 3.1锁定前缀本机使用 3.2线程#1或#2首先到达,赢得比赛。 3.3获胜线程比较(是aBoolean==true?)这将返回"true",因此一个布尔值将被设置为"false"。 3.4 aBoolean现在为false。 3.5线程丢失

  • 我正在用C编写一个程序。为了简单起见,我们可以说:有几个变量,许多线程都可以读写。每次写入其中一个时,它都是通过原子交换(GCC原子操作、同步和交换)写入的。我是否需要在每次读取其中一个变量时使用原子负载,或者原子写入是否足以避免在写入过程中读取数据? 注意,需要使用其中一个变量的数据的任何位置都会首先复制值: 我的问题不是关于数据竞赛,也就是说我不担心我会丢失数据。我担心的是,在我阅读它的过程中