C++11引入了标准化的内存模型,但这到底意味着什么?它将如何影响C++编程?

这篇文章(加文·克拉克引用赫伯·萨特的话)说,

内存模型意味着C++代码现在有一个标准化的图书馆可以调用无论编译器是谁制造的以及在哪个平台上运行。有一种标准的方法来控制不同的线程与处理器的内存。“当你谈论分裂时[代码]跨越不同的核心在标准中,我们谈论的是记忆模型。我们将在不破坏以下假设萨特说。

嗯,我可以在网上记住这段和类似的段落(因为我从出生起就有自己的记忆模型:P),甚至可以发帖回答别人提出的问题,但老实说,我并不完全理解这一点。

C++程序员以前就开发过多线程应用程序,那么,是POSIX线程、Windows线程还是C++11线程又有什么关系呢?有什么好处?我想了解底层细节。

我还感觉到,C++11内存模型与C++11多线程支持有某种关系,正如我经常看到的那样。如果是,具体如何?为什么它们应该是相关的?

我不知道多线程的内部工作原理,也不知道内存模型的一般含义。


当前回答

这意味着该标准现在定义了多线程,并定义了在多线程环境中发生的事情。当然,人们使用了不同的实现,但这就像是在问我们为什么要使用std::string,而我们都可以使用一个原生的string类。

当你谈论POSIX线程或Windows线程时,这是一种错觉,因为实际上你谈论的是x86线程,因为这是一个并发运行的硬件函数。C++0x内存模型提供了保证,无论您使用的是x86、ARM、MIPS还是其他任何您能想到的东西。

其他回答

对于没有指定内存模型的语言,您正在为处理器体系结构指定的语言和内存模型编写代码。处理器可以选择为性能重新排序存储器访问。因此,如果您的程序存在数据竞争(数据竞争是指多个内核/超线程可以同时访问同一内存),那么您的程序不会跨平台,因为它依赖于处理器内存模型。您可以参考Intel或AMD软件手册,了解处理器如何重新排序内存访问。

非常重要的是,锁(以及带有锁的并发语义)通常以跨平台的方式实现。。。因此,如果在没有数据竞争的多线程程序中使用标准锁,那么就不必担心跨平台内存模型。

有趣的是,Microsoft C++编译器为volatile提供了获取/释放语义,这是一个C++扩展,用于解决C++中缺少内存模型的问题http://msdn.microsoft.com/en-us/library/12a04hfd(v=vs.80).aspx。然而,鉴于Windows仅在x86/x64上运行,这并不是什么大不了的(Intel和AMD内存模型使得在一种语言中实现获取/释放语义变得简单高效)。

C和C++过去是由格式良好的程序的执行轨迹定义的。

现在,它们一半由程序的执行轨迹定义,一半由同步对象上的许多排序定义。

这意味着这些语言定义根本没有意义,因为没有逻辑方法来混合这两种方法。特别是,互斥体或原子变量的破坏没有得到很好的定义。

这意味着该标准现在定义了多线程,并定义了在多线程环境中发生的事情。当然,人们使用了不同的实现,但这就像是在问我们为什么要使用std::string,而我们都可以使用一个原生的string类。

当你谈论POSIX线程或Windows线程时,这是一种错觉,因为实际上你谈论的是x86线程,因为这是一个并发运行的硬件函数。C++0x内存模型提供了保证,无论您使用的是x86、ARM、MIPS还是其他任何您能想到的东西。

这是一个已有多年历史的问题,但非常受欢迎,值得一提的是,这是一份了解C++11内存模型的绝佳资源。我认为总结他的演讲是没有意义的,以便做出另一个完整的答案,但考虑到这是真正编写标准的人,我认为很值得观看演讲。

赫伯·萨特(Herb Sutter)就C++11内存模型进行了长达三个小时的演讲,题为“原子武器”(atomic<>Weapons),可在第9频道网站YouTube上获得-第1部分和第2部分。这场演讲技术性很强,涉及以下主题:

优化、竞赛和内存模型订购–内容:获取和发布订购–方式:互斥、原子和/或围栏对编译器和硬件的其他限制代码生成和性能:x86/x64、IA64、POWER、ARM放松原子学

演讲没有详细阐述API,而是讨论了推理、背景、幕后和幕后(你知道吗,放松的语义只是因为POWER和ARM不能有效地支持同步加载而被添加到标准中的吗?)。

如果您使用互斥锁来保护所有数据,您真的不必担心。互斥体始终提供足够的排序和可见性保证。

现在,如果您使用原子或无锁算法,则需要考虑内存模型。内存模型精确地描述了原子何时提供排序和可见性保证,并为手工编码保证提供了可移植的围栏。

以前,原子性将使用编译器内部函数或某些更高级别的库来完成。围栏将使用CPU特定指令(内存屏障)完成。