我什么时候真的需要使用原子<bool>而不是bool?

80 c++ atomicity c++11 stdatomic

不是atomic<bool>多余的,因为bool它本质上是原子的吗?我认为不可能有部分修改的bool值.我什么时候真的需要用atomic<bool>而不是bool

Ker*_* SB 87

C++中的任何类型都不是"原子性的",除非它是一个std::atomic*东西.那是因为标准这样说.

在实践中,为操纵a std::atomic<bool>而发出的实际硬件指令可能(或可能不)与普通硬件指令相同bool,但是原子是更大的概念,具有更广泛的分支(例如对编译器重新排序的限制).此外,一些操作(如否定)在原子操作上过载,以在硬件上创建与非原子变量的本机非原子读 - 修改 - 写序列明显不同的指令.

  • 小修正,`std :: atomic_flag`是唯一的例外,虽然它的名字也以atomic开头. (13认同)
  • @yngccc:我认为这就是Kerrek SB编写`std :: atomic*`而不是`std :: atomic <*>`的原因. (4认同)

Dim*_*ims 61

记住记忆障碍.尽管可能无法bool部分更改,但多处理器系统可能在多个副本中具有此变量,并且即使在另一个线程将其更改为新的后,一个线程也可以看到旧值.Atomic引入了内存屏障,因此变得不可能.

  • 为了清楚起见.@Vincent的评论可能源于对Java中关键字`volatile`的理解.Java中的`volatile`关键字确实控制了内存栅栏,但与C中的`volatile`关键字有着截然不同的行为.[这个问题](http://stackoverflow.com/questions/19923352/whats-the-difference-of-the-usage-of-volatile-between-cc-and-c-java)进一步解释了这一区别. (9认同)
  • 不,易失性与记忆栅栏无关. (6认同)
  • 关键字`volatile`可以修复多处理器问题吗? (3认同)
  • 我认为这是真正正确的答案.因为关于"标准bla-bla-bla ... sizeof(bool)可以> 1"的答案在现实生活中从未发生过.所有主要编译器都有sizeof(bool)== 1,并且所有读/写操作都将以类似的方式用于bool和atomic <bool>.但是,对于任何现代应用和硬件来说,多核CPU和错过的内存屏障几乎都有100%的可能性 (2认同)

Pet*_*ker 24

C++的原子类型处理三个潜在的问题.首先,如果操作需要多个总线操作,那么读取或写入可能被任务切换器撕裂(并且可能发生在a bool,具体取决于它的实现方式).其次,读取或写入可能仅影响与正在执行操作的处理器相关联的高速缓存,并且其他处理器可能在其高速缓存中具有不同的值.第三,如果编译器不影响结果,编译器可以重新排列操作的顺序(约束有点复杂,但现在已经足够了).

您可以通过明确刷新缓存以及使用特定于编译器的选项来防止重新排序来假设您正在使用的类型是如何实现的,从而自己处理这三个问题中的每一个(并且,不,volatile不执行此操作)除非您的编译器文档说它确实).

但为什么要经历这一切呢?atomic为你照顾它,并且可能比你自己做得更好.


And*_*zos 19

考虑比较和交换操作:

bool a = ...;
bool b = ...;

if (a)
    swap(a,b);
Run Code Online (Sandbox Code Playgroud)

在我们读完之后,我们得到了真的,另一个线程可能会出现并设置为false,然后我们交换(a,b),因此在退出b之后为false,即使交换已经完成.

使用std::atomic::compare_exchange我们可以原子地执行整个if/swap逻辑,这样另一个线程就无法在if和swap之间设置为false(没有锁定).在这种情况下,如果进行交换,则b在退出时必须是假的.

这只是适用于两种值类型(如bool)的原子操作的一个示例.

  • 为什么这是评分最低的答案?这(或std :: atomic_flag中的test_and_set)是使用原子布尔类型的主要原因。 (2认同)

hus*_*had 18

原子操作不仅仅是撕裂的价值,所以虽然我同意你和其他海报,我不知道bool有可能出现撕裂的环境,但还有更多危险.

Herb Sutter对此进行了精彩的讨论,您可以在线查看.请注意,这是一个漫长而有牵连的谈话.Herb Sutter,Atomic Weapons.问题归结为避免数据争用,因为它允许您具有顺序一致性的错觉.


Ale*_*aev 8

某些类型的原子性完全取决于底层硬件.每个处理器体系结构对某些操作的原子性有不同的保证.例如:

Intel486处理器(以及之后的新处理器)保证始终以原子方式执行以下基本内存操作:

  • 读或写一个字节
  • 读取或写入在16位边界上对齐的字
  • 读取或写入在32位边界上对齐的双字

其他架构对于哪些操作是原子的具有不同的规范.

C++是一种高级编程语言,力求从底层硬件中抽象出来.出于这个原因,标准根本不允许人们依赖这种低级别的假设,否则您的应用程序将无法移植.因此,C++中的所有原始类型atomic都由符合C++ 11标准的开箱即用的标准库提供.

  • 另一个关键部分是,C++ 编译器通常允许将变量保留在寄存器中或优化访问,因为它们可以假设没有其他线程正在更改该值。(由于数据竞争 UB)。`atomic` 有点包含 `volatile` 的这个属性,所以 `while(!var){}` 不能优化为 `if(!var)infinite_loop();`。参见[MCU编程-C++ O2优化中断while循环](// electronics.stackexchange.com/a/387478) (2认同)