因为本质上是原子的,所以不是atomic<bool>
多余的吗?bool
我认为不可能有部分修改的 bool 值。我什么时候真的需要使用atomic<bool>
而不是bool
?
6 回答
C++ 中的任何类型都不是“本质上是原子的”,除非它是std::atomic*
-something。那是因为标准是这样说的。
在实践中,用于操作的实际硬件指令std::atomic<bool>
可能(或可能不)与普通的相同bool
,但原子性是一个更大的概念,具有更广泛的影响(例如,对编译器重新排序的限制)。此外,一些操作(如否定)在原子操作上被重载,以在硬件上创建与非原子变量的本机、非原子读-修改-写序列截然不同的指令。
记住记忆障碍。尽管可能无法bool
部分更改,但多处理器系统可能在多个副本中具有此变量,并且即使在另一个线程将其更改为新值之后,一个线程仍可以看到旧值。Atomic 引入了内存屏障,因此它变得不可能。
C++ 的原子类型处理三个潜在问题。首先,如果操作需要多个总线操作(这可能发生在 abool
上,取决于它的实现方式),任务切换可能会破坏读取或写入。其次,读取或写入可能只影响与正在执行操作的处理器关联的缓存,而其他处理器的缓存中可能有不同的值。第三,如果不影响结果,编译器可以重新排列操作的顺序(约束有点复杂,但现在就足够了)。
您可以通过假设您正在使用的类型是如何实现的、通过显式刷新缓存以及使用特定于编译器的选项来防止重新排序(并且,不,volatile
不这样做)来自行处理这三个问题中的每一个除非你的编译器文档说是这样)。
但为什么要经历这一切呢?atomic
会为您照顾好它,并且可能比您自己做的更好。
考虑一个比较和交换操作:
bool a = ...;
bool b = ...;
if (a)
swap(a,b);
在我们读取 a 之后,我们得到了 true,另一个线程可能会出现并设置 a false,然后我们交换 (a,b),所以在退出 b 之后是 false,即使交换已经完成。
使用我们可以原子地std::atomic::compare_exchange
执行整个 if/swap 逻辑,这样其他线程就不能在 if 和 swap 之间将 a 设置为 false(没有锁定)。在这种情况下,如果进行了交换,则 b 在退出时必须为假。
这只是适用于 bool 等二值类型的原子操作的一个示例。
原子操作不仅仅是关于撕裂值,所以虽然我同意你和其他发帖人的观点,我不知道bool
可能存在撕裂的环境,但风险更大。
Herb Sutter 对此进行了精彩的演讲,您可以在线查看。请注意,这是一个漫长而复杂的谈话。赫伯萨特,原子武器。问题归结为避免数据竞争,因为它让您产生顺序一致性的错觉。
某些类型的原子性完全取决于底层硬件。每个处理器架构对某些操作的原子性都有不同的保证。例如:
Intel486 处理器(以及之后的更新处理器)保证以下基本内存操作将始终以原子方式执行:
- 读取或写入一个字节
- 读取或写入在 16 位边界上对齐的字
- 读取或写入在 32 位边界上对齐的双字
其他架构对哪些操作是原子的有不同的规范。
C++ 是一种高级编程语言,它致力于将您从底层硬件中抽象出来。出于这个原因,标准根本不允许人们依赖这种低级别的假设,否则您的应用程序将无法移植。因此,C++ 中的所有原始类型atomic
都由符合 C++11 标准的开箱即用标准库提供。