c ++,std :: atomic,什么是std :: memory_order以及如何使用它们?

260*_*607 66 c++ multithreading atomic memory-model stdatomic

任何人都可以用简单的英语解释什么是std :: memory_order,以及如何将它们与std :: atomic <>一起使用?

我在这里找到了参考文献和一些例子,但根本不了解. http://en.cppreference.com/w/cpp/atomic/memory_order

谢谢.

Ant*_*ams 36

这些std::memory_order值允许您为原子操作提供的内存排序指定细粒度约束.如果要从多个线程修改和访问原子变量,那么将std::memory_order值传递给操作可以放松编译器和处理器上关于这些原子变量的操作对其他线程可见的顺序以及同步的约束这些操作对应用程序中的非原子数据产生的影响.

默认排序std::memory_order_seq_cst是最受约束的,并提供您可能期望的"直观"属性:如果线程A存储一些数据然后使用设置原子标志std::memory_order_seq_cst,那么如果线程B看到标志已设置,那么它可以看到写入的数据通过线程A.其他内存排序值不一定提供此保证,因此必须非常小心地使用.

其基本前提是:不使用比其他任何东西std::memory_order_seq_cst(默认),除非(一)你真的真的知道自己在做什么,并能证明该轻松的使用是安全的在所有情况下,和(b)您的探查表明,您打算使用轻松排序的数据结构和操作是瓶颈.

我的书" C++ Concurrency in Action"将整整一章(45页)用于C++内存模型,原子操作和std::memory_order约束的细节,以及在无锁数据结构中使用原子操作进行同步的另一章(44页) ,以及轻松排序约束的后果.

我在Dekker算法Peterson的互斥算法博客文章展示了一些问题.

  • 放松是非常安全的 - 它真正需要它 - 读取没有依赖/相应值的孤立值,因此没有订购问题的风险.例如,要求线程停止的`bool shouldHalt`.根据我的经验,这种隔离在正常的软件开发中并不罕见.我认为,对于另一种情况(多个变量之间的依赖关系),锁定首先几乎总是更好.我认为人们对此有困难的唯一方法是使用花哨的原子链接数据结构,除非有严重的需要,否则这是值得注意的. (2认同)

je4*_*e4d 23

任何人都可以用简单的英语解释什么是std :: memory_order,

我在各种记忆顺序中找到的最好的"普通英语"解释是Bartoz Milewski关于轻松原子的文章:http://bartoszmilewski.com/2008/12/01/c-atomics-and-memory-ordering/

后续帖子:http://bartoszmilewski.com/2008/12/23/the-inscrutable-c-memory-model/

但请注意,虽然这些文章是一个很好的介绍,但它们早于C++ 11标准,并不会告诉您安全使用它们需要知道的一切.

以及如何将它们与std :: atomic <>一起使用?

我在这里给你的最好建议是:不要.轻松的原子(可能)是C++ 11中最棘手和最危险的东西.坚持std::atomic<T>默认的内存排序(顺序一致性),直到你真的,确定你有一个可以通过使用轻松的内存顺序解决的性能问题.

在上面的第二篇文章中,Bartoz Milewski得出以下结论:

在尝试推理C++弱原子时,我不知道自己是在做什么.它们背后的理论是如此复杂,以至于它的边界无法使用.花了三个人(安东尼,汉斯和我)和标准的修改来完成一个相对简单的算法的证明.想象一下,基于弱原子的无锁队列做同样的事情!

  • 轻松`更容易理解:仅在需要原子性而不需要同步时使用它。例如,对于单个原子计数器,多个线程将增加,但这对于其他任何数据是否有效没有任何意义。 (2认同)

zvr*_*rba 16

不可以."普通英语"解释需要32页,可以在这里找到.

如果您不想阅读它,您可以忘记内存排序,因为您链接的页面表示默认是顺序一致的排序,这是"始终做得很好" - 设置.

要使用其他任何设置,您真的有阅读和理解上述文件及其中的例子.


edA*_*a-y 5

简而言之,您的编译器和CPU可能会按照与编写指令不同的顺序执行指令.对于单线程,这不是问题,因为它看起来是正确的.对于多个处理器上的多个线程,这成为一个问题.C++中的内存排序限制了编译器/ CPU可以执行的操作并修复了此类问题.

例如,如果你看看我的关于双重检查锁定的文章,你可以看到如何使用该模式排序混乱 - 它提到显示原子内存排序可以用来修复它.

关于重新排序本身,您还可以考虑CPU重新排序 - 再次,编译器也可能正在进行重新排序.

请注意,有关此主题(包括我的)的任何文档都会提到理论情景.最常见的CPU,如x86,具有非常强大的排序保证,因此根本不需要大量的显式排序.因此,即使您没有使用正确的C++ 11原子,您的代码仍可能仍然有效.

正如zvrba所提到的,这个话题实际上非常详细.关于内存屏障的linux内核doc 也包含很多详细信息.