并发性:C ++ 11内存模型中的原子和易失性 [英] Concurrency: Atomic and volatile in C++11 memory model

查看:137
本文介绍了并发性:C ++ 11内存模型中的原子和易失性的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

全局变量在2个不同核心上的2个并发运行的线程中共享。线程写入和读取变量。对于原子变量,一个线程可以读取一个陈旧的值?每个核心可能在其缓存中具有共享变量的值,并且当一个线程在缓存中写入其副本时,不同核心上的另一个线程可能从其自己的缓存读取失效值。或者编译器强大的内存排序从其他缓存读取最新的值? c ++ 11标准库具有std :: atomic支持。这与volatile关键字有何不同?

A global variable is shared across 2 concurrently running threads on 2 different cores. The threads writes to and read from the variables. For the atomic variable can one thread read a stale value? Each core might have a value of the shared variable in its cache and when one threads writes to its copy in a cache the other thread on a different core might read stale value from its own cache. Or the compiler does strong memory ordering to read the latest value from the other cache? The c++11 standard library has std::atomic support. How this is different from the volatile keyword? How volatile and atomic types will behave differently in the above scenario?

推荐答案

首先, volatile 不意味着原子访问。它设计用于像内存映射I / O和信号处理。与 std :: atomic 一起使用时, volatile 完全不必要,除非您的平台另有说明,否则 volatile 对线程之间的原子访问或内存排序没有影响。

Firstly, volatile does not imply atomic access. It is designed for things like memory mapped I/O and signal handling. volatile is completely unnecessary when used with std::atomic, and unless your platform documents otherwise, volatile has no bearing on atomic access or memory ordering between threads.

如果您有一个在线程之间共享的全局变量,例如:

If you have a global variable which is shared between threads, such as:

std::atomic<int> ai;

那么可见性和排序约束取决于用于操作的内存排序参数,

then the visibility and ordering constraints depend on the memory ordering parameter you use for operations, and the synchronization effects of locks, threads and accesses to other atomic variables.

在没有任何附加同步的情况下,如果一个线程写入一个值到 ai 然后没有什么保证另一个线程将看到任何给定时间段的值。该标准规定它应该是可见的在合理的时间段,但任何给定的访问可能返回一个陈旧的值。

In the absence of any additional synchronization, if one thread writes a value to ai then there is nothing that guarantees that another thread will see the value in any given time period. The standard specifies that it should be visible "in a reasonable period of time", but any given access may return a stale value.

默认的内存排序 std :: memory_order_seq_cst 为所有变量中的所有 std :: memory_order_seq_cst 操作提供单个全局总排序。这并不意味着你不能得到陈旧的值,但它的确意味着你得到的值决定,并由你的操作所在的总顺序的位置决定。

The default memory ordering of std::memory_order_seq_cst provides a single global total order for all std::memory_order_seq_cst operations across all variables. This doesn't mean that you can't get stale values, but it does mean that the value you do get determines and is determined by where in this total order your operation lies.

如果您有两个共享变量 x y ,最初为零,并且有一个线程写入1到 x ,另一个写入2到 y 读取可以看到(0,0),(1,0),(0,2)或(1,2),因为在操作之间没有排序约束,因此操作可以以任何顺序出现在全局

If you have 2 shared variables x and y, initially zero, and have one thread write 1 to x and another write 2 to y, then a third thread that reads both may see either (0,0), (1,0), (0,2) or (1,2) since there is no ordering constraint between the operations, and thus the operations may appear in any order in the global order.

如果两个写入都来自同一个线程, x = 1 之前 y = 2 ,然后读取线程在 x 之前读取 y )不再是有效选项,因为 y == 2 的读取意味着对 x 的早期写入是可见。其他3个配对(0,0),(1,0)和(1,2)仍然可能,取决于2个读取如何与2个写入交错。

If both writes are from the same thread, which does x=1 before y=2 and the reading thread reads y before x then (0,2) is no longer a valid option, since the read of y==2 implies that the earlier write to x is visible. The other 3 pairings (0,0), (1,0) and (1,2) are still possible, depending how the 2 reads interleave with the 2 writes.

如果您使用其他内存排序,例如 std :: memory_order_relaxed std :: memory_order_acquire 此外,单个全局排序不再适用。如果没有额外的同步,线程甚至不必同意两个商店的顺序来分离变量。

If you use other memory orderings such as std::memory_order_relaxed or std::memory_order_acquire then the constraints are relaxed even further, and the single global ordering no longer applies. Threads don't even necessarily have to agree on the ordering of two stores to separate variables if there is no additional synchronization.

唯一的方法,以保证你有最新值是使用读取修改写入操作,例如 exchange() compare_exchange_strong() fetch_add()。读 - 修改 - 写操作具有附加的约束,它们总是对最新值操作,因此一系列线程的 ai.fetch_add(1)将返回没有重复或间隙的值序列。在没有其他约束的情况下,仍然不能保证哪些线程会看到哪些值。

The only way to guarantee you have the "latest" value is to use a read-modify-write operation such as exchange(), compare_exchange_strong() or fetch_add(). Read-modify-write operations have an additional constraint that they always operate on the "latest" value, so a sequence of ai.fetch_add(1) operations by a series of threads will return a sequence of values with no duplicates or gaps. In the absence of additional constraints, there's still no guarantee which threads will see which values though.

使用原子操作是一个复杂的主题。我建议你阅读很多背景材料,并在用atomics编写生产代码之前检查已发布的代码。在大多数情况下,编写使用锁的代码更容易,效率不会明显降低。

Working with atomic operations is a complex topic. I suggest you read a lot of background material, and examine published code before writing production code with atomics. In most cases it is easier to write code that uses locks, and not noticeably less efficient.

这篇关于并发性:C ++ 11内存模型中的原子和易失性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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