106

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

4

6 回答 6

111

C++ 中的任何类型都不是“本质上是原子的”,除非它是std::atomic*-something。那是因为标准是这样说的。

在实践中,用于操作的实际硬件指令std::atomic<bool>可能(或可能不)与普通的相同bool,但原子性是一个更大的概念,具有更广泛的影响(例如,对编译器重新排序的限制)。此外,一些操作(如否定)在原子操作上被重载,以在硬件上创建与非原子变量的本机、非原子读-修改-写序列截然不同的指令。

于 2013-05-01T15:23:38.147 回答
82

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

于 2013-05-01T15:36:07.163 回答
31

C++ 的原子类型处理三个潜在问题。首先,如果操作需要多个总线操作(这可能发生在 abool上,取决于它的实现方式),任务切换可能会破坏读取或写入。其次,读取或写入可能只影响与正在执行操作的处理器关联的缓存,而其他处理器的缓存中可能有不同的值。第三,如果不影响结果,编译器可以重新排列操作的顺序(约束有点复杂,但现在就足够了)。

您可以通过假设您正在使用的类型是如何实现的、通过显式刷新缓存以及使用特定于编译器的选项来防止重新排序(并且,不,volatile不这样做)来自行处理这三个问题中的每一个除非你的编译器文档说是这样)。

但为什么要经历这一切呢?atomic会为您照顾好它,并且可能比您自己做的更好。

于 2013-05-01T16:18:48.543 回答
29

考虑一个比较和交换操作:

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 等二值类型的原子操作的一个示例。

于 2013-05-01T15:38:18.210 回答
21

原子操作不仅仅是关于撕裂值,所以虽然我同意你和其他发帖人的观点,我不知道bool可能存在撕裂的环境,但风险更大。

Herb Sutter 对此进行了精彩的演讲,您可以在线查看。请注意,这是一个漫长而复杂的谈话。赫伯萨特,原子武器。问题归结为避免数据竞争,因为它让您产生顺序一致性的错觉。

于 2013-05-01T15:29:45.733 回答
10

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

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

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

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

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

于 2013-05-01T15:35:23.050 回答