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

时间:2008-10-21 04:19:39

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

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

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

7 个答案:

答案 0 :(得分:34)

C ++内存模型是关于C ++代码读/写物理内存的时间和原因的规范。

直到下一个C ++标准,C ++内存模型与C相同。在C ++ 0x标准中,预计将包含适用于多线程的内存模型(参见here),它将可能是C标准下一版本C1X的一部分。目前的基础是初步的:

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

因此,当前状态是:C ++内存操作仅在您有1个进程时指定,其主线程并且不编写依赖于变量读/写的特定顺序的代码,就是这样。从本质上讲,这意味着除了传统的你好世界计划之外你还搞砸了。

当然,你会立即添加“它今天在我的机器上运行,你不可能是正确的”。正确的句子将是“它今天在我的机器上运行,具有硬件,操作系统(线程库)和编译器的特定组合,他们彼此了解得足以实现有些工作但可能会在某些时候破坏的东西“

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

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

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

答案 1 :(得分:23)

看到其他一些答案,似乎很多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标准提案。

答案 2 :(得分:2)

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

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

答案 3 :(得分:2)

如何查看C ++标准委员会网站上的论文:

答案 4 :(得分:1)

如果您想更深入地了解共享内存一致性模型,我将向您推荐以下教程。

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

答案 5 :(得分:-1)

简短回答:没有

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

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

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

答案 6 :(得分:-8)

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

  • 向下增长的堆栈 - 也就是说,当您推送堆栈帧时,堆栈指针的值小于

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

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

  • 内存分配器malloc,以8位字节分配内存。 New还分配内存,但它分配的内存量取决于要新建的对象的大小。

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

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

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

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

有更多细节,其中许多细节特定于特定的硬件,操作系统或线程系统,但这是必不可少的想法。