【问题标题】:What is the C++03 memory model for concurrency?并发的 C++03 内存模型是什么?
【发布时间】:2010-09-18 06:10:43
【问题描述】:

C++03中并发的内存模型是什么?

(而且,C++11 是否更改了内存模型以更好地支持并发?)

【问题讨论】:

  • 你应该重写问题更清楚
  • 我同意 - 这个问题很重要,但措辞非常糟糕。我会努力改进的。 (声誉游戏咆哮:我觉得具有讽刺意味的是,这个问题被标记为社区 wiki - 这意味着没有代表回答,无论他们可能多么深入、技术或好)
  • 我觉得社区wiki系统不错。问题是提出这个问题的人是新手,由于社区 wiki 的实际功能对新手来说并不清楚,因此许多人将其设为 wiki,因为它听起来很酷。当我从这里开始时,我做了完全相同的事情:)

标签: c++ concurrency c++03 memory-model


【解决方案1】:

C++ 内存模型是针对 C++ 代码读取/写入物理内存的时间和原因的规范。

在下一个 C++ 标准之前,C++ 内存模型与 C 相同。在 C++0x 标准中,预计将包含一个适当的多线程内存模型(参见here),它将成为一部分可能是 C 标准的下一个修订版,C1X。当前的一个是简陋的:

  • 它只指定当前程序可观察到的内存操作的行为。
  • 当多个进程访问同一内存时,它没有说明并发内存访问(没有共享内存或进程的概念)。
  • 当多个线程访问同一内存时,它没有说明并发内存访问的任何内容(没有线程的概念)。
  • 它无法指定内存访问的顺序(编译器优化包括代码移动和最近的处理器重新排序访问,两者都可能破坏模式,例如双重检查初始化)。

因此,当前状态是:仅当您有 1 个进程、其主线程且不编写取决于变量读/写的特定顺序的代码时才指定 C++ 内存操作,仅此而已。从本质上讲,这意味着除了传统的 hello world 程序之外,您已经完蛋了。

当然,系统会提示您添加 “它今天在我的机器上运行,你不可能是对的”。正确的句子是 “它今天可以在我的机器上使用这种特定的硬件、操作系统(线程库)和编译器组合,它们彼此了解足够多,可以实现一些有点工作但可能会在某些时候中断的东西".

好吧好吧,这有点苛刻,但是地狱,even Herb Sutter acknowledges that(只需阅读介绍)他正在谈论最普遍的 C/C++ 工具链之一的所有 2007 年之前的版本......

C++ 标准委员会试图想出一些东西来解决所有这些问题,同时仍然比 Java 的内存模型限制更少(因此性能更好)。

Hans Boehm 收集了here 一些关于该问题的论文,包括学术论文和 C++ 委员会的论文。

【讨论】:

  • 我觉得你在这里有点苛刻。 C++/C 是大多数实时、关键任务、多线程嵌入式系统的首选语言。虽然 C++ 标准不支持 MT,但没有任何 C++ 编译器供应商会销售无法在 MT 环境中使用的工具。
  • 你说得对,我重读了自己,这听起来很刺耳。但是,如果您还没有,我建议您阅读此内容:open-std.org/JTC1/sc22/wg21/docs/papers/2007/n2197.pdf 所有 C/C++ 工具链都受此问题影响。
  • 根据 C 语言,双重检查模式非常好。不幸的是,理论与实践不符,因为真正的处理器会重新排序。
  • 对不起,但是像“根据 C 的双重检查模式很好”这样的断言是无稽之谈,原因在帖子中给出。随着内存模型的开发,我认为默认情况下也不好,但应该有一种方法可以使编译器产生所需的障碍(通过所谓的“低级原子”,IIRC)。
  • 这是真的:“C++0x”和 C1x 将是第一个对使用线程时发生的事情进行任何说明的 C++ 和 C 标准。没有这个,真的会发生非常糟糕的事情:请参阅博客条目thegibson.org/blog/archives/23“内存排序和内存模型”(以及链接的 LKML 和 GCC 列表线程),了解当没有规则告诉编译器实现者时会发生什么情况的示例他们能做什么和不能做什么。所以,当然,现在很多事情似乎都奏效了,但这主要是运气......
【解决方案2】:

看到其他一些答案,似乎许多 C++ 程序员甚至不知道您所询问的“内存模型”是什么意思。

问题是关于某种意义上的内存模型:关于写入/读取重新排序(可能发生在编译器端或运行时端)有什么保证(如果有的话)?这个问题对于多线程编程非常重要,因为如果没有这样的规则,编写正确的多线程程序是不可能的,而且有些令人惊讶的事实是,由于当前缺乏显式内存模型,许多多线程程序或多或少地“靠运气”工作——通常要归功于编译器在函数调用之间假设指针别名。 - 见Threads Cannot be Implemented as a Library

在当前的 C++ 中,没有标准的内存模型。一些编译器为 volatile 变量定义内存模型,但这是非标准的。 C++0x 为此目的定义了新的“原子”原语。可以在Threads and memory model for C++找到查看最近状态的详尽起点

重要的链接还有Concurrency memory modelAtomic TypesC++ Data-Dependency Ordering: Atomics and Memory Model标准提案。

【讨论】:

    【解决方案3】:

    不幸的是,在 C++ 中没有像 Java 那样的“标准内存模型”。实际的实现由编译器、运行时库和处理器决定。

    因此,C++ 内存模型 == 混沌混杂的模型,这意味着您始终必须尝试编写不依赖于特定内存模型的安全代码,这也适用于线程编程,因为编译器可以在临界区之外进行任何它想要的优化,甚至是乱序处理!

    【讨论】:

    • 这是不正确的。除了有一个工作堆栈和一个堆外,没有假定内存模型。该模型不是一个混乱的混合体,它只是硬件上的任何东西。 IE,哈佛还是冯诺依曼,没有堆,堆。它是基于硬件的,这表示它的顺序。
    • @Vlion:你错了。内存模型是对物理内存何时以及为何被读取/写入的描述。硬件在这里基本上是无关紧要的,除了指定的内容必须在当前硬件上可以实现!
    • 感谢 blue.tuxedo 在那里报道我 ;) 对于那些不熟悉该术语本身的人,我的回答应该更具体。
    【解决方案4】:

    看看 C++ 标准委员会网站上的论文怎么样:

    ?

    【讨论】:

      【解决方案5】:

      如果您想更深入地了解共享内存一致性模型,建议您参考以下教程。

      http://rsim.cs.uiuc.edu/~sadve/Publications/computer96.pdf

      【讨论】:

        【解决方案6】:

        简答:没有

        长答案:C++ 没有托管内存,您必须自己分配和释放它。智能指针类可以减轻这种负担。如果您忘记释放分配的内存,那就是内存泄漏和错误。如果您在释放内存后尝试使用内存,或者您多次尝试释放内存,这些也是令人讨厌的错误。

        至于底层细节,C++ 没有具体说明——这取决于硬件。内存是通过指针访问的,其中包含某种内存地址。内存地址可以是物理地址或虚拟地址。如果您正在使用操作系统内核,或者您正在阅读以实模式运行的旧 DOS 代码,您只会看到物理地址。更多详情,请阅读virtual memory,那里有很多很好的资源。

        x86 架构还允许使用段描述符来寻址内存。这是一大堆蠕虫,自 Win16 时代以来就没有真正使用过,如果你幸运的话,你永远不必处理它。

        【讨论】:

          【解决方案7】:

          简而言之,C++ 内存模型包括...

          • 向下增长的堆栈——也就是说,当您压入堆栈帧时,堆栈指针的值小于原来的值

          • 向上增长的堆,即新分配的内存的结束地址比之前的内存大。您使用 malloc() 或 new 在堆中分配内存。如果堆中没有足够的可用内存,则 malloc(或 new)调用系统函数 brk() sbrk() 以增加堆的大小。如果对 brk() 或 sbrk() 的调用失败,则 malloc 或 new 将失败并出现内存不足异常。

          您永远不需要关心堆栈或堆是向下还是向上增长,在某些系统中它们可能以相反的方式运行。只需考虑堆栈和堆从地址空间的末端向内增长。

          • 内存分配器 malloc,它以 8 位字节分配内存。 New 也会分配内存,但它分配的内存量是基于被 new 对象的大小。

          • 包含可执行代码的文本空间。文本位于堆下方。执行期间不能更改文本空间

          程序可能在文本下方有其他特殊用途的部分。

          您可以在 linux 系统上使用 objdump 查看程序是如何静态组织的(在加载之前)。

          我注意到,虽然您没有在问题中提及它,但“并发”是您分配给此问题的关键字之一。线程系统在堆上为每个线程分配额外的线程空间,然后管理堆栈指针以在线程之间切换。

          还有更多细节,其中许多是特定于特定硬件、操作系统或线程系统的,但这是基本概念。

          【讨论】:

          • 堆栈增长的方向取决于硬件架构,而不是任何 C++ 内存模型。
          • 你是对的。可能我没有写清楚,但我就是这么说的。
          猜你喜欢
          • 2013-08-12
          • 2014-02-06
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-02-22
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多