首先,volatile
并不意味着原子访问。它是为诸如内存映射I/O和信号处理之类的东西而设计的。volatile
在一起使用时完全没有必要std::atomic
,除非你的平台文件另有规定,volatile
对线程之间的原子访问或内存排序没有影响。
如果有一个在线程之间共享的全局变量,如:
std::atomic<int> ai;
然后,可见性和排序约束取决于用于操作的内存排序参数,以及锁、线程和对其他原子变量的访问的同步效果。
在没有任何附加同步的情况下,如果一个线程将一个值写入ai
然后,没有任何东西可以保证另一个线程在任何给定的时间段内都会看到该值。该标准规定“在合理的时间内”它应该是可见的,但是任何给定的访问都可能返回一个陈旧的值。
的默认内存顺序。std::memory_order_seq_cst
为所有用户提供一个单一的全局总订单。std::memory_order_seq_cst
所有变量的操作。这并不意味着您不能得到陈旧的值,但它确实意味着您得到的值决定并由您的操作在这个总顺序中的位置决定。
如果有两个共享变量x
和y
,最初为零,并有一个线程将1写到x
另一个写2到y
,读取这两个操作的第三个线程可能会看到(0,0)、(1,0)、(0,2)或(1,2),因为操作之间没有排序约束,因此操作可能以全局顺序出现在任何顺序。
如果两个写入都来自同一个线程,则x=1
以前y=2
读取线程读取y
以前x
则(0,2)不再是有效的选项,因为y==2
意味着先前写到x
是可见的。其他3对(0,0)、(1,0)和(1,2)仍然是可能的,这取决于2对与2写的交织方式。
如果使用其他内存顺序,如std::memory_order_relaxed
或std::memory_order_acquire
这样,约束就会进一步放松,单一的全局排序就不再适用了。如果没有额外的同步,线程甚至不必就两个存储的顺序来分离变量达成一致。
确保您拥有“最新”值的唯一方法是使用读-修改-写入操作,例如exchange()
, compare_exchange_strong()
或fetch_add()
..读-修改-写入操作有一个附加约束,它们总是对“最新”值进行操作,因此ai.fetch_add(1)
由一系列线程进行的操作将返回一个没有重复或空白的值序列。在没有附加约束的情况下,仍然无法保证哪些线程会看到哪些值。
使用原子操作是一个复杂的主题。我建议您阅读大量背景材料,并在使用Atomics编写产品代码之前检查已发布的代码。在大多数情况下,编写使用锁的代码更容易,而且效率也不会明显降低。