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

is multiple assignments of atomic variables, an atomic operation?

假设我有两个原子布尔值,如下所示。

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>

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

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

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

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

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

相同:CPU必须访问两个[=22的值=]different 变量,通过将值复制到它自己的寄存器中,进行布尔求值,取反,并执行求值²;显然不是原子操作。

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

不,不是。原子操作所保证的只是变量上不会发生任何干预操作。在您的示例中,完全有可能 y 被分配,一些不相关的事情发生(但仅在另一个线程中;在当前线程中,由于 operator= 隐含的内存栅栏,不会发生重新排序atomic),然后 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.