std::atomic 究竟是什么? [英] What exactly is std::atomic?

查看:45
本文介绍了std::atomic 究竟是什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我知道 std::atomic<> 是一个原子对象.但是原子到什么程度呢?据我了解,操作可以是原子的.使对象原子化到底是什么意思?例如,如果有两个线程并发执行以下代码:

I understand that std::atomic<> is an atomic object. But atomic to what extent? To my understanding an operation can be atomic. What exactly is meant by making an object atomic? For example if there are two threads concurrently executing the following code:

a = a + 12;

那么整个操作(比如add_twelve_to(int))是原子的吗?或者是否对变量 atomic 进行了更改(所以 operator=())?

Then is the entire operation (say add_twelve_to(int)) atomic? Or are changes made to the variable atomic (so operator=())?

推荐答案

std::atomic<> 表示不同线程可以同时操作(它们的实例)的类型,而不会引发未定义的行为:

Each instantiation and full specialization of std::atomic<> represents a type that different threads can simultaneously operate on (their instances), without raising undefined behavior:

原子类型的对象是唯一没有数据竞争的 C++ 对象;也就是说,如果一个线程写入一个原子对象而另一个线程从它读取,则行为是明确定义的.

Objects of atomic types are the only C++ objects that are free from data races; that is, if one thread writes to an atomic object while another thread reads from it, the behavior is well-defined.

此外,对原子对象的访问可以建立线程间同步并按照std::memory_order指定的顺序对非原子内存访问进行排序.

In addition, accesses to atomic objects may establish inter-thread synchronization and order non-atomic memory accesses as specified by std::memory_order.

std::atomic<> 包装了在 C++ 之前的 11 次中必须使用(例如)互锁函数与 MSVC 或 atomic bultins 在 GCC 的情况下.

std::atomic<> wraps operations that, in pre-C++ 11 times, had to be performed using (for example) interlocked functions with MSVC or atomic bultins in case of GCC.

此外,std::atomic<> 通过允许各种 内存顺序,指定同步和排序约束.如果您想阅读有关 C++ 11 原子和内存模型的更多信息,这些链接可能有用:

Also, std::atomic<> gives you more control by allowing various memory orders that specify synchronization and ordering constraints. If you want to read more about C++ 11 atomics and memory model, these links may be useful:

请注意,对于典型用例,您可能会使用重载算术运算符另一组:

Note that, for typical use cases, you would probably use overloaded arithmetic operators or another set of them:

std::atomic<long> value(0);
value++; //This is an atomic op
value += 5; //And so is this

因为操作符语法不允许你指定内存顺序,这些操作将使用 std::memory_order_seq_cst,因为这是 C++ 11 中所有原子操作的默认顺序.它保证所有原子操作之间的顺序一致性(总全局排序).

Because operator syntax does not allow you to specify the memory order, these operations will be performed with std::memory_order_seq_cst, as this is the default order for all atomic operations in C++ 11. It guarantees sequential consistency (total global ordering) between all atomic operations.

然而,在某些情况下,这可能不是必需的(并且没有什么是免费的),因此您可能需要使用更明确的形式:

In some cases, however, this may not be required (and nothing comes for free), so you may want to use more explicit form:

std::atomic<long> value {0};
value.fetch_add(1, std::memory_order_relaxed); // Atomic, but there are no synchronization or ordering constraints
value.fetch_add(5, std::memory_order_release); // Atomic, performs 'release' operation

现在,您的示例:

a = a + 12;

不会计算为单个原子操作:它将导致 a.load()(它本身是原子的),然后在此值和 12 之间添加和a.store()(也是原子的)最终结果.正如我之前提到的,这里将使用 std::memory_order_seq_cst.

will not evaluate to a single atomic op: it will result in a.load() (which is atomic itself), then addition between this value and 12 and a.store() (also atomic) of final result. As I noted earlier, std::memory_order_seq_cst will be used here.

然而,如果你写a += 12,它将是一个原子操作(正如我之前提到的)并且大致相当于a.fetch_add(12, std::memory_order_seq_cst).

However, if you write a += 12, it will be an atomic operation (as I noted before) and is roughly equivalent to a.fetch_add(12, std::memory_order_seq_cst).

至于您的评论:

常规 int 具有原子加载和存储.用 atomic<> 包装它有什么意义?

A regular int has atomic loads and stores. Whats the point of wrapping it with atomic<>?

您的陈述仅适用于为存储和/或加载提供这种原子性保证的架构.有些架构不这样做.此外,通常要求必须在字/双字对齐的地址上执行操作才能成为原子 std::atomic<> 是保证每个 平台,无额外要求.此外,它允许您编写这样的代码:

Your statement is only true for architectures that provide such guarantee of atomicity for stores and/or loads. There are architectures that do not do this. Also, it is usually required that operations must be performed on word-/dword-aligned address to be atomic std::atomic<> is something that is guaranteed to be atomic on every platform, without additional requirements. Moreover, it allows you to write code like this:

void* sharedData = nullptr;
std::atomic<int> ready_flag = 0;

// Thread 1
void produce()
{
    sharedData = generateData();
    ready_flag.store(1, std::memory_order_release);
}

// Thread 2
void consume()
{
    while (ready_flag.load(std::memory_order_acquire) == 0)
    {
        std::this_thread::yield();
    }

    assert(sharedData != nullptr); // will never trigger
    processData(sharedData);
}

请注意,断言条件将始终为真(因此永远不会触发),因此您始终可以确保在 while 循环退出后数据已准备就绪.那是因为:

Note that assertion condition will always be true (and thus, will never trigger), so you can always be sure that data is ready after while loop exits. That is because:

  • store() 标记在 sharedData 设置后执行(我们假设 generateData() 总是返回一些有用的东西,特别是, 从不返回 NULL) 并使用 std::memory_order_release 顺序:
  • store() to the flag is performed after sharedData is set (we assume that generateData() always returns something useful, in particular, never returns NULL) and uses std::memory_order_release order:

memory_order_release

具有此内存顺序的存储操作执行释放操作:当前线程中没有读或写可以重新排序之后这家店.当前线程中的所有写入都可见获取相同原子变量的其他线程

A store operation with this memory order performs the release operation: no reads or writes in the current thread can be reordered after this store. All writes in the current thread are visible in other threads that acquire the same atomic variable

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆