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

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

91822 次浏览

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

In practice, the actual hardware instructions that are emitted to manipulate an std::atomic<bool> may (or may not) be the same as those for an ordinary bool, but being atomic is a larger concept with wider ramifications (e.g. restrictions on compiler re-ordering). Furthermore, some operations (like negation) are overloaded on the atomic operation to create a distinctly different instruction on the hardware than the native, non-atomic read-modify-write sequence of a non-atomic variable.

Atomic operations are about more than just torn values, so while I agree with you and other posters that I am not aware of an environment where torn bool is a possibility, there is more at stake.

Herb Sutter 做了一个很棒的演讲,你可以在线观看。警告你,这是一个漫长而复杂的谈话。Herb Sutter 原子武器部.这个问题归结为避免数据竞争,因为这会让你产生循序一致性的错觉。

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

Intel486处理器(以及更新的处理器)保证下列基本内存操作总是自动执行:

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

其他体系结构具有不同的规范,在这些规范上,操作是原子的。

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

记住 memory barriers。虽然不可能部分地更改 bool,但是多处理器系统可能有多个副本中的这个变量,即使在另一个线程将其更改为 new 之后,一个线程也可以看到旧值。原子引入了记忆屏障,所以这是不可能的。

Consider a compare and exchange operation:

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


if (a)
swap(a,b);

在我们读取 a 之后,我们得到 true,另一个线程可能会出现并设置一个 false,然后我们交换(a,b) ,所以在退出 b 之后,即使交换已经完成,也是 false。

使用 std::atomic::compare_exchange,我们可以执行完整的 if/swap 逻辑 原子级的,这样其他线程就不能在 if 和交换之间设置 a 为 false (没有锁定)。在这种情况下,如果进行了互换,那么 b 在退出时必须为假。

This is just one example of an atomic operation that applies to a two value type such as bool.

C + + 的原子类型处理 潜在的问题。首先,如果操作需要一个以上的总线操作(而且 可以发生在 bool上,这取决于它的实现方式) ,那么读或写操作可能会被任务交换机破坏。其次,读或写操作可能只影响与执行该操作的处理器相关联的缓存,而其他处理器的缓存中可能有不同的值。第三,如果操作不影响结果,编译器可以重新排列操作的顺序(约束稍微复杂一些,但目前这已经足够了)。

You can deal with each of these three problems on your own by making assumptions about how the types you are using are implemented, by explicitly flushing caches, and by using compiler-specific options to prevent reordering (and, no, volatile doesn't do this unless your compiler documentation says it does).

但是为什么要经历这些呢?atomic为你照顾它,而且可能比你自己能做的更好。