原子变量的多重赋值是原子操作吗?

is multiple assignments of atomic variables, an atomic operation?

本文关键字:原子操作 赋值 变量      更新时间:2023-10-16

考虑我有两个原子布尔值,如下所示。

private:
std::atomic_bool x;
std::atomic_bool y;

我可以说下面的操作是原子的吗?还是我必须使用lock_guard来确保它们一起分配?

x = y = true; // are two bools assigned together atomically?

还要考虑在另一个线程中我想读取这些布尔值。

if(!x && !y) ...

我的假设是这不是原子的,也许最好使用atomic<int>代替?

不,不是。原子操作所保证的只是不会对变量进行干预操作。在您的示例中,完全有可能y被分配,发生一些不相关的事情(但仅在另一个线程中;在当前线程中,由于原子上operator=暗示的内存围栏,不会发生重新排序(,然后分配x。阅读它们时也是如此。

如果确实希望这些操作是原子操作,则需要使用封装两条信息的单个原子类型。有很多方法可以做到这一点;您可以使用 char 并以某些位掩码操作为代价使用不同的位,您可以使用 16 位整数,但我将使用最清晰的(恕我直言(方法进行说明:具有两个布尔值的结构。

struct MyBools {
bool x;
bool y;
};
bool operator==(const MyBools& lhs, const MyBools& rhs) {
return lhs.x == rhs.x && lhs.y == rhs.y;
}
using MyAtomicBools = std::atomic<MyBools>;
MyAtomicBools b{true, true};
...
if (b == MyBools{false, false}) { ... }

这可能会也可能不会优化,也可以使用16位整数并手动抛弃两个布尔值。 GCC 似乎对此进行了非常好的优化; 它将设置操作转换为单个写入 + 内存围栏,但 Clang 做得不尽如人意:https://godbolt.org/g/moiT9Y。

x = y = true; // are two bools assigned together atomically?

这条线显然不是原子操作,因为 x 和 y 位于内存中的两个不同位置:不可能同时设置两个不连续³的位置。

原子字意味着读取或写入在一个 cpu 周期¹ 内完成,因此一个变量是安全的,但 x 和 y 是两个不同的原子变量。

如果您有任何疑问,请随时查看通过使用反汇编器生成的二进制代码。

if(!x && !y) ...

相同:CPU必须通过将两个不同变量的值复制到自己的寄存器中来访问这两个变量的值,进行布尔计算,否定并执行评估²;显然不是原子操作。

¹ 它肯定不是那么简单,但从更高的语言开发者的角度来看,你应该认为
² 它又不是那么简单,因为编译器可以进行优化,而 CPU 可以自己做一些事情
³ 即使有连续的位置,总大小也必须在一个循环中可读/可写: 1Mo 的数据显然无法由 CPU 在一个循环中读取,即使所有数据都连续并排。