C + + 11 引入标准化的内存模型中,但是准确的含义是什么呢?以及它将如何影响 c + + 编程?

等 Sutter 说这里

内存模型意味着 c + + 代码,现在有一个标准化的库调用无论谁做了编译器如何以及在什么平台上运行。没有不同的标准方法,控制线程与处理器的内存。

"当你谈拆分 [代码] 给不同的标准中的核心时,我们还谈论了内存模型。我们将对它进行优化而不会破坏人打算在代码中进行了以下的假设,"Sutter 说。

好吧,我可以记住这和类似段落可联机 (因为我自出生以来我自己内存模型︰ P),甚至张贴回答所提问题的其他人,可以但老实说,我不完全理解这。

因此,我基本上是想知道是,c + + 程序员用来开发多线程应用程序,甚至之前,那么,如何有关系如果 POSIX 线程,或 Windows 线程或 C + + 11 线程?好处是什么?我想要了解的低级别的详细信息。

我还获得 C + + 11 内存模型某种程度上与此感觉 C + + 11 多线程支持,因为我常常看到它们两个在一起。如果是这样,究竟如何?为什么应该它们相关?

因为我不知道如何内核的多线程的工作方式,以及哪些内存模型意味着一般而言,请帮助我理解这些概念。:-)

2011-06-11 23:30:14
问题评论:

回答:

首先,您必须了解语言律师的角度考虑。

C + + 规范不会对任何特定的编译器、 操作系统或 CPU 的引用。它可以使参考是实际系统的归纳抽象机在语言律师领域中,程序员的工作是编写代码的抽象机;编译器的工作是 actualize 具体机器上的代码。通过编码严格的规范,可以确保您的代码将编译并无需修改任何在系统上运行与符合标准的 c + + 编译器,是否今天或 50 年,从现在。

在 C + + 98 / C + + 03 规范抽象机基本上是单线程的。因此,就不可能编写多线程的 c + + 代码,是"完全可移植的"关于规范。规范不甚至是任何有关原子性的内存负载情况和商店或订单中加载和存储可能会发生这种情况,都更别提诸如此类互斥锁。

当然,您可以在实践中对于特定的具体系统 — 如 pthreads 或 Windows 编写多线程的代码。但是,没有标准的方法来编写多线程的代码的 C + + 98 / C + + 03。

在 C + + 11 抽象机器是多线程的设计。它还具有定义完善的内存模型;也就是说,它说编译器可能并访问内存时,可能不会进行。

请考虑下面的示例中,其中一对全局变量被两个线程同时访问︰

           Global
           int x, y;

Thread 1            Thread 2
x = 17;             cout << y << " ";
y = 37;             cout << x << endl;

线程 2 可能输出?

在 C + + 98 / C + + 03,这不是甚至未定义的行为;问题本身是没有意义的因为标准不考虑任何东西称为"线索"。

在 C + + 11,结果是未定义的行为,因为加载和存储不需要原子一般。这似乎是很大的改进...和其本身而言,它不是。

但是,C + + 11,您可以编写这︰

           Global
           atomic<int> x, y;

Thread 1                 Thread 2
x.store(17);             cout << y.load() << " ";
y.store(37);             cout << x.load() << endl;

现在,事情就变得更有趣。首先,这里的行为是定义现在可以打印线程 2 0 0 (如果运行之前线程 1), 37 17 (如果它运行线程 1 之后),或0 17 (如果其在线程 1 分配给 x,但之前它将分配给 y 后运行)。

它不能打印是37 0,这是因为 C + + 11 中的原子加载/存储的默认模式是以强制执行顺序的一致性这就意味着所有加载和存储必须"仿佛"碰巧在笔记中每个线程,虽然可以在线程间操作的顺序交错的系统但是喜欢。因此原子结构的默认行为提供原子性顺序加载和存储。

现在,在现代的 CPU,确保连续的一致性可能很昂贵。特别是,编译器很可能发出之间每个访问此处的全面内存屏障。但如果您的算法可以容忍无序加载和存储;亦即,如果它需要原子性但不是排序;亦即,如果它能够容忍37 0作为此程序的输出,然后您可以写这︰

           Global
           atomic<int> x, y;

Thread 1                            Thread 2
x.store(17,memory_order_relaxed);   cout << y.load(memory_order_relaxed) << " ";
y.store(37,memory_order_relaxed);   cout << x.load(memory_order_relaxed) << endl;

更多的现代 CPU、 更有可能这是比前面的示例更快。

最后,如果您只需要按顺序保留特定的加载和存储,您可以编写︰

           Global
           atomic<int> x, y;

Thread 1                            Thread 2
x.store(17,memory_order_release);   cout << y.load(memory_order_acquire) << " ";
y.store(37,memory_order_release);   cout << x.load(memory_order_acquire) << endl;

我们因此恢复到有序的负载和存储此采用37 0不再可能的输出,但它会使用最小的开销。(在此简单示例中,结果是全面的顺序一致性相同; 在较大的程序中,它不会)。

当然,如果您想要查看的唯一的输出是0 037 17,就可以换行原始代码周围的互斥体。但是,如果您已经阅读了这到目前为止,我敢打赌,您已经知道了它的工作原理,并且此答案已经长的比我所预期的:-)。

这样,底线。互斥体是很好,和 C + + 11 标准化它们了。但有时出于性能原因希望较低级别原语 (例如,经典双重检查锁定模式)。新的标准提供了高级互斥锁和条件变量,像的小工具,它还提供低级别的小工具,如原子类型和各种口味的内存屏障。现在,您可以编写全部在所指定的标准、 语言复杂、 高性能并发例程,可以确保您的代码将编译并运行上当今系统和明日的不变。

虽然要 frank,除非您是专家,并从事一些严重的低级别代码,您应该可能坚持使用互斥锁和条件变量。这就是我想要做。

有关这类内容的详细信息,请参见以下博客文章.

很好的答案,但这真的急新原语一些实际范例。此外,我认为内存排序不基元与前相同的 C + + 0x︰ 没有任何保证。

@ 约翰︰ 我知道,但我仍然我自己学习基元:-)。我也认为它们保证字节访问是原子 (尽管未按顺序排列) 这就是我为我的示例中就"char"原因...但我甚至不敢 100%确定...如果您想要建议任何好的"教程"引用我会将它们添加到我的答案

@Nawaz︰ 是的 !内存访问可以通过编译器或 CPU 会被重新排序。考虑一下 (举例来说) 缓存和推理的负载。在哪个系统内存获取命中可以执行任何操作的顺序如下所示您的编码。编译器和 CPU 将确保这种重新排序不会中断单线程代码。对于多线程代码,可能的重新排序,如果两个线程将同一位置读取/写入一次会发生什么和您 excert 对两者的控制的特点是"内存模型"。对于单线程代码,内存模型无关。

@Nawaz、 @Nemo-一个小细节︰ 新的内存模型是单线程代码中相关,只要它如指定某些表达式,undefinedness i = i++序列点的旧概念已被丢弃;新标准指定同一件事使用已排序的前关系它是只是一种特殊的情况,更一般的线程间的发生-前概念。

@AJG85︰ 规范草案 C + + 0x 3.6.2 节说,"变量与静态存储持续时间 (3.7.1) 或线程存储持续时间 (3.7.2) 应会初始化为零的 (8.5) 的任何其他初始化发生之前。由于 x,y 是全球在此示例中,它们具有静态存储持续时间,因此将初始化为零的我相信。

只需将与我理解内存一致性模型 (或内存模型,简称) 的相似之处。它是由"时间,时钟和分布式系统中的事件排序"Leslie Lamport 创作性纸张了。比喻这种情况往往比较简练和有根本的意义,但可能会对于许多人而言显得大材小用。但是,我希望它提供了促进关于内存一致性模型的推理的心理图像 (图形化表示形式)。

让我们查看时空图在水平轴表示地址空间中的所有内存位置的历史记录 (即,每个内存位置由该轴上的点表示) 和垂直轴表示的时间 (我们将看到,一般情况下,不存在时间的通用概念)。持有的每个内存位置的值的历史记录,因此,用该内存地址处的垂直列。其中一个线程将新值写入到该位置的每个值的更改。通过一个内存中的映像,将意味着什么呢聚合/组合的值的所有内存位置可观察到在特定时间特定线程.

"内存一致性和缓存一致性初级读本"报价

直观的 (和最严格的) 内存模型是顺序一致性 (SC) 的多线程的执行应如下所示隔行扫描的每个组成部分线程的顺序执行线程像时间-多路复用的单核处理器上。

全局内存顺序可能会因到另一个程序运行,并可能预先不知道。SC 的特征功能是 simultaneity 的地址空间-时间图表示平面(即内存映像) 中的水平切层的一套。在给定平面,所有其事件 (或内存值) 都是同时进行。有的绝对时间概念,在达成一致意见的所有线程的内存值是同时进行。在 SC,在即时,每次都只能有一个内存中的映像的所有线程共享。,在每个时刻的时间,所有处理器对内存映像 (即内存聚合内容) 达成都一致。这不但并不意味着所有线程都查看同一个值序列的所有内存位置,还要确保所有处理器都观察所有变量的相同值的组合这是说︰ 所有的内存操作 (在所有内存位置) 总顺序观察到由所有线程相同。

在宽松的内存模型中,每个线程将切片地址空间-时间以自己的方式,唯一的限制是,因为所有的线程必须达成每个单个内存位置的历史记录,每个线程切片应不能跨越彼此 (当然,不同的线程 5 月并将,彼此间)。通用方法切分它了 (没有特权的编页的地址空间时)。切片不需要是平面 (或线性)。他们可以弯曲,这是什么可以使线程读取顺序写在另一个线程写入的值。不同的内存位置的历史记录可能滑 (或获得拉伸) 随意彼此查看任何特定的线程时每个线程都将具有事件 (或等效,内存值) 是同时发生的不同意义。同时向一个线程的事件 (或内存值) 设置不可同时到另一个。因此,在宽松的内存模型中,所有线程仍然观察都到每个内存位置的相同历史的记录 (即,值序列)。但他们可能会发现不同的内存映像 (即所有内存位置值的组合)。即使两个不同的内存位置,由同一个线程按顺序编写的这两个新写入的值,可能会看到不同的顺序由其他线程。

[从维基百科的图片]Picture from Wikipedia

读者熟悉的爱因斯坦的特殊理论的相关性会注意到我在 alluding 的到。将 Minkowski 的词翻译成内存模型领域︰ 地址空间和时间是地址-空间-时间的阴影。在这种情况下,每个观察者 (即线程) 将投射上阴影的事件 (例如,内存存储/加载) 他自己世界线 (亦即,他的时间轴) 和 simultaneity (他的地址空间轴) 他自己平面。线程中的 C + + 11 内存模型对应于观察者移动相对于彼此在特殊的相对性的。顺序一致性对应于Galilean 的时空(即所有观察者事件的一个绝对的顺序和达成全球意义上的 simultaneity)。

内存模型和特殊的相对性之间类似归功于这一事实既定义部分排列的一组事件,通常称为因果集。某些事件 (例如,内存存储区) 会影响 (但不是会影响) 的其他事件。C + + 11 线程 (或在物理学中的观察者) 只是一系列 (即完全有序集) 事件 (例如,内存加载和存储区可能具有不同的地址)。

中的相对性,某种顺序还原到部分有序事件,看似混乱的图片,由于只有临时排序所有的观察者达成是"timelike"事件 (即,这些事件原则上通过转慢光在真空中的速度比任何粒子 connectible) 间的顺序。Invariantly 的 timelike 相关的事件按时间顺序。在物理学中,Craig Callender 的时间.

在 C + + 11 内存模型中,类似的机制 (获取-释放一致性模型) 用于建立这些本地的因果关系.

若要放弃 SC 提供内存一致性的定义和动机,我将报价从"初级读本上内存一致性和缓存一致性"

对于共享的内存计算机内存一致性模型定义其内存系统的体系结构上可见行为。单处理器核心的正确性标准分区之间"一个正确的结果","很多不正确的备选方案"的行为。这是因为处理器的体系结构要求执行线程将给定的输入的状态转换为单个定义明确的输出状态,即使是在无序的核心上。共享的内存一致性模型,但是,考虑加载和存储的多个线程并且通常不允许很多 (更多) 错误的同时允许许多正确执行有多个正确执行的可能性是由于 ISA 允许多个线程同时执行,往往有许多可能的法律 interleavings 的从不同的线程的指令。

宽松的或较弱的内存一致性模型被运用强模型中的大多数内存排序都是不必要的事实。如果一个线程更新数据的十个项目,然后同步标志,程序员通常不介意如果按照顺序彼此进行更新的数据项目,但仅前更新所有的数据项标志将更新 (通常使用实现围墙的说明)。轻松的模型试图捕获此增加订购灵活性和保留只有订单这程序员"要求"以获得更高的性能和正确性的资深大律师。例如,在某些体系结构中,FIFO 写缓冲区用于由每个核心将结果写入到缓存之前举行的承诺 (退休) 存储结果。这种优化来提高性能,但违反资深大律师。写入缓冲区隐藏服务店小姐的延迟。因为商店是常见的可以避免大部分的他们在拖延是一个重要的好处。单核心处理器,写入缓冲区可建筑上无形通过确保负荷对地址 A 到 A 返回最近存储的值,即使一个或多个存储到 A 写入缓冲区中。这通常是通过任一绕最近存储的值到 A 到负载从 A,在"最近"是由程序顺序,或决定拖延 A 负载,如果一家商店到 A 写入缓冲区中。当使用多个内核时,都会有它自己绕过写缓冲区。没有写入缓冲区,硬件是 SC,但与写入缓冲区,它不是使写入缓冲区中的多核处理器架构上可见。

存储存储重新排序可能发生核是否允许非 FIFO 写入缓冲区存储部门比它们的输入的顺序不同的顺序。如果在第二个命中时缓存未命中第一个存储,或者与早期 (即前第一存储区中) 存储可以合并的第二个存储,这可能会发生。负载的负载进行重新排序,也可能出现在执行指令顺序程序的动态调度的核心。可以重新排列上另一个核心存储相同行为 (可以得出两个线程示例交替?)。重新排序更高版本的存储库与早期负载 (负载存储区重新排序) 会导致很多不正确的行为,例如释放锁来保护它 (如果存储是解除锁定操作) 后加载值。注意存储负载重新排序也可能会出现由于本地绕过普遍实施的先进先出写入缓冲区中即使有按程序顺序执行的所有指令的核心。

缓存一致性和内存一致性有时会让人混淆,因为它是有益也有此报价单︰

与一致性,不同缓存一致性是既不可见的软件,也不需要。设法隐藏的共享内存系统缓存不如作为缓存中的单核系统的一致性。正确的一致性将确保程序员不能确定是否以及在何处系统中安装了缓存通过分析加载和存储结果。这是因为正确的一致性确保了缓存永远不会使新的或不同功能性行为 (程序员可能仍能够推断可能缓存结构使用计时信息)。高速缓存一致性协议的主要目的维护单个编写器-多-阅读器 (SWMR) 不变量的每个内存位置。一致性和一致性之间的重要区别是一致性指定每个内存位置为基础,而对于所有内存位置指定的一致性。

继续我们的心理图片,固定 SWMR 对应于最有一个粒子在任何一个位置的物理要求,但可以有无限的多个观察者的任何位置。

+ 1 为具有特殊相关性的相似之处,我已一直在尝试自己做的相同相似之处。经常看研究试图解释行为中出现的不同线程的操作按特定的顺序,相互交错的线程的代码的程序员,我不得不告诉他们,不对与多处理器系统的不同 < s > 框架参考 < /s > 线程之间的 simultaneity 概念是现在毫无意义。比较具有特殊的相对性是一个好的方法,以使其遵守问题的复杂性。

@Ahmed Nassar︰ 从斯坦福共享的链接已死。

@Joze︰ 谢谢。我让它改为引用 ACM 库。它是在 Web 上的其他地方仍然免费提供。

因此应断定宇宙是多核?

@PeterK︰ 完全:)这就是非常好的可视化的这张图片的时间由著名的物理学 Brian 格林︰ youtube.com/watch?v=4BjGWLJNPcA (& t) = 22m12s分钟 22 和 12 秒处是"视觉效果的时间 [完整纪录片]"。

它的意思是,现在标准定义多线程,它定义了多个线程的上下文中会发生什么情况。当然,人们使用的不同实现,但是,就像问为什么我们应该有std::string ,当我们无法全部使用家庭回退string类。当您正在谈论 POSIX 线程或 Windows 线程时,然后这是幻觉位实际上您说的是关于 x86 的线程,因为它是同时运行的硬件函数。C + + 0x 内存模型则的保证,无论您身在 x86,或 ARM,Mips,或您可以拿出任何其他操作。

Posix 线程并不限于 x86。事实上,他们已在实施的第一个系统可能不是 x86 的系统。Posix 线程是独立于系统和在所有 Posix 平台上有效。它也是不真的如此它是硬件属性,因为 Posix 线程还可以实现通过协作式多任务处理。但大多数线程问题当然只表面上硬件线程实现 (和一些甚至仅在多处理器/多核系统上)。

对于未指定内存模型的语言,您编写的代码的语言指定的处理器体系结构的内存模型。选择处理器可能会重新排序性能的内存访问。这样,如果您的程序具有数据争用(数据争用时为多个内核可能 / 超线程可以同时访问同一个内存) 然后程序不跨平台由于其依赖于处理器内存模型。您可以参阅英特尔或 AMD 软件手册以了解处理器可能会重新排序的内存访问。

更为重要的是,锁定 (和并发语义与锁定) 通常会实现跨平台方式...因此,如果您在使用多线程程序中使用标准锁没有数据争用然后您不必担心跨平台内存模型.

有趣的是,Microsoft c + + 编译器已获得 / 释放是一个 c + + 扩展来处理 c + + 中的内存模型缺乏易失性的语义http://msdn.microsoft.com/en-us/library/12a04hfd(v=vs.80).aspx.但是,给定窗口运行在 x86 / 只 x64,,不说得多 (Intel 和 AMD 内存模型更加轻松、 高效地实现获取 / 释放语义在语言中)。

这现在是一个 2 岁的问题,但在非常受欢迎,值得一提的极好的学习资源有关的 C + + 11 内存模型。我看到合计以使此另一个完整的答案,他谈话没有必要,但假定这是实际编写了一些标准,我认为它的专家也值得一看你们的谈话。

等 Sutter 具有 C + + 11 内存模型的长谈论题为"原子的 <> 武器",在 3 小时可上 Channel9 网站-第 1 部分第 2 部分讲话是非常技术性的并包括以下主题︰

  1. 优化,比赛和内存模型
  2. 订购 – 什么︰ 获取和释放
  3. 订购 – 如何︰ 互斥锁、 原子结构,和/或范围
  4. 编译器和硬件上的其他限制
  5. 代码生成和性能︰ x86/x64,IA64、 电源、 ARM
  6. 宽松的原子结构

讲话不详细阐述的 api,但在推理,而是在汽车发动机罩下,幕后背景 (您知道只是因为电源和臂不支持同步的负载高效宽松的语义添加到标准?)。

该讲话确实棒极了,完全值得将花观看它 3 个小时。

@ZunTzu︰ 在大多数视频播放器,您可以设置速度为 1.25,1.5 或甚至 2 次原始。

请输入您的翻译

C++11 introduced a standardized memory model. What does it mean? And how is it going to affect C++ programming?

确认取消