C ++ 11引入了标准化的内存模型。这是什么意思?它将如何影响C ++编程?
|
C ++ 11引入了标准化的内存模型,但这到底是什么意思?它将如何影响C ++编程?
这篇文章(由加文·克拉克(Gavin Clarke)引用赫伯·萨特)表示:
内存模型意味着C ++代码
现在有一个标准库可供调用
不管谁编译
以及它在哪个平台上运行。
有一种标准方法可以控制
不同的线程交谈
处理器的内存。
\“当您谈论拆分时
跨不同核心的[代码]
在标准中,我们正在谈论
内存模型。我们准备去
优化它而不破坏
以下假设人们会去
编写代码,”萨特说。
好吧,我可以记住这一段以及网上可以找到的类似段落(因为我自出生以来就有自己的记忆模型:P),甚至可以发布它作为对其他人提出的问题的答案,但是说实话,我没有完全明白这一点。
C ++程序员甚至以前都曾开发过多线程应用程序,那么,这是POSIX线程,Windows线程还是C ++ 11线程又有什么关系呢?有什么好处?我想了解底层细节。
我也感觉到C ++ 11内存模型与C ++ 11多线程支持某种程度上相关,因为我经常将两者结合在一起。如果是,究竟是什么?为什么要联系它们?
由于我不知道多线程的内部原理以及内存模型的一般含义,请帮助我理解这些概念。 :-)
没有找到相关结果
已邀请:
8 个回复
体悉
线程2可能输出什么? 在C ++ 98 / C ++ 03下,这甚至不是“未定义行为”;这个问题本身是没有意义的,因为该标准并未考虑任何称为“线程”的事物。 在C ++ 11下,结果是未定义行为,因为加载和存储通常不需要是原子的。看起来似乎并没有太大的改善...就其本身而言,不是。 但是,使用C ++ 11,您可以编写以下代码:
现在事情变得更加有趣了。首先,在这里定义行为。线程2现在可以打印
(如果在线程1之前运行),)3ѭ(如果在线程1之后运行)或
(如果在线程1分配给x之后但在分配给y之前运行)。 它不能打印的是“ 5”,因为C ++ 11中原子加载/存储的默认模式是强制顺序一致性。这只是意味着所有加载和存储必须“好像”在您在每个线程中写入的顺序进行,而线程之间的操作可以交错,但是系统会喜欢。因此,原子的默认行为既提供了原子性,又提供了装载和存储的顺序。 现在,在现代CPU上,确保顺序一致性可能很昂贵。特别是,编译器很可能在每次访问之间发出完全成熟的内存屏障。但是,如果您的算法可以容忍乱序的加载和存储;即,如果它需要原子性但不需要排序;也就是说,如果它可以容忍此程序的输出为“ 5”,那么您可以这样编写:
CPU越现代化,比上一个示例的运行速度越快。 最后,如果只需要按顺序保留特定的装入和存储,则可以编写:
这将我们带回到有序的装载和存储状态,因此不再有可能输出“ 5”,但是这样做的开销却很小。 (在这个简单的示例中,结果与成熟的顺序一致性相同;在较大的程序中,结果则不是。) 当然,如果要查看的唯一输出是
或
,则只需在原始代码周围包裹一个互斥体即可。但是,如果您已经读了那么多书,我敢打赌,您已经知道它是如何工作的,并且这个答案已经比我打算的要长:-)。 因此,底线。互斥体很棒,并且C ++ 11对其进行了标准化。但有时出于性能原因,您需要较低级别的原语(例如,经典的双重检查锁定模式)。新标准提供了诸如互斥锁和条件变量之类的高级小工具,还提供了诸如原子类型和各种不同的内存屏障之类的低级小工具。因此,现在您可以完全使用标准指定的语言编写复杂的高性能并发例程,并且可以确定您的代码可以在当今和未来的系统上编译并保持不变。 坦率地说,除非您是专家并且致力于一些严肃的低级代码,否则您应该坚持使用互斥体和条件变量。这就是我打算做的。 有关这些内容的更多信息,请参见此博客文章。
缔恃钨
咖哀烈
骚瓤
类时,为什么我们应该有
。 当您谈论POSIX线程或Windows线程时,这实际上是您在谈论x86线程,这是一种错觉,因为它是同时运行的硬件功能。无论您使用的是x86,ARM,MIPS还是其他任何可用的东西,C ++ 0x内存模型都可以保证。
稍惮
死簇
翰冒绢县
起初接缝不直观,
和
必须为
。如果它们不是原子的,则可以在写入它们的同时(
)同时读取它们(
)。根据C ++内存模型,即使
从未实际使用数据,这也是一场竞赛。另外,如果它们不是原子的,则编译器可以将每个值的第一次读取缓存在寄存器中。显然,您不希望...要在
中的
循环的每次迭代中重新读取。 使它们成为
,并以
访问它们也是不够的。这样做的原因是seq的读取(在ѭ19中)仅具有获取语义。简单来说,如果X和Y是内存访问,X在Y之前,X不是获取或释放,并且Y是获取,则编译器可以在X之前对Y进行重新排序。如果Y是seq的第二次读取,并且X如果是读取数据,那么这种重新排序将破坏锁的实现。 本文给出了一些解决方案。今天性能最好的一个可能是在第二次读取seqlock之前使用
和
的那个。在本文中,它是图6。在这里,我不会在这里复制代码,因为到目前为止已经读过本文的人都应该阅读本文。它比这篇文章更加精确和完整。 最后一个问题是使29号变量成为原子级可能是不自然的。如果您无法编写代码,则需要非常小心,因为从非原子转换为原子仅对原始类型合法。 C ++ 20应该加上30,这将使此问题更容易解决。 总结一下:即使您认为自己了解C ++内存模型,在滚动自己的序列锁之前也应该非常小心。
扭湘阀柿蹄