std :: atomic到底是什么? [英] What exactly is std::atomic?

查看:581
本文介绍了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=())?

推荐答案

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步中必须使用的操作(例如)

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:

  • C++ atomics and memory ordering
  • Comparison: Lockless programming with atomics in C++ 11 vs. mutex and RW-locks
  • C++11 introduced a standardized memory model. What does it mean? And how is it going to affect C++ programming?
  • Concurrency in C++11

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

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()(它本身是原子),然后在该值与最终结果的12a.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);
}

请注意,断言条件将始终为true(因此将永远不会触发),因此您始终可以确保在退出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

    while循环退出之后使用
  • sharedData,因此在load() from标志之后将返回非零值. load()使用std::memory_order_acquire顺序:
    • sharedData is used after while loop exits, and thus after load() from flag will return a non-zero value. load() uses std::memory_order_acquire order:
    • std::memory_order_acquire

      具有此存储顺序的加载操作执行 acquire 操作 在受影响的内存位置上:当前无读写 可以在此加载之前重新对线程进行重新排序. 所有其他线程中的写入 释放相同原子变量的变量在当前可见 线程.

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

      这使您可以精确地控制同步,并允许您明确指定代码可能/可能/不会/将/将不起作用的方式.如果仅保证原子性本身,这将是不可能的.特别是当涉及到非常有趣的同步模型时,例如发布-消费排序.

      This gives you precise control over the synchronization and allows you to explicitly specify how your code may/may not/will/will not behave. This would not be possible if only guarantee was the atomicity itself. Especially when it comes to very interesting sync models like the release-consume ordering.

      这篇关于std :: atomic到底是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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