Multithreading 处理无序执行

Multithreading 处理无序执行,multithreading,Multithreading,我最近偶然发现了这一点。根据我在多线程方面的经验,我意识到由于程序能够随时在线程之间切换线程而导致的许多问题。然而,我从来不知道编译器和硬件优化可以以一种保证对单个线程有效的方式对操作进行重新排序,但对多线程不一定有效。有人能解释一下如何在多线程环境中正确处理重新排序操作的可能性吗 更新:我最初无意中链接到了这篇文章,而不是这篇文章,这篇文章对问题有了更好的解释 您如何防止执行功能失效的可能性发生并在您的脸上爆炸 不可以-编译器只能在不改变最终结果的情况下更改执行顺序。您不应该在不同线程中按顺序

我最近偶然发现了这一点。根据我在多线程方面的经验,我意识到由于程序能够随时在线程之间切换线程而导致的许多问题。然而,我从来不知道编译器和硬件优化可以以一种保证对单个线程有效的方式对操作进行重新排序,但对多线程不一定有效。有人能解释一下如何在多线程环境中正确处理重新排序操作的可能性吗

更新:我最初无意中链接到了这篇文章,而不是这篇文章,这篇文章对问题有了更好的解释

您如何防止执行功能失效的可能性发生并在您的脸上爆炸


不可以-编译器只能在不改变最终结果的情况下更改执行顺序。

您不应该在不同线程中按顺序运行需要执行的操作。线程是用于并行处理事情的,因此如果顺序很重要,则需要串行处理。

现在大多数编译器都有显式的内部排序。C++0x也有内存排序内部函数。

编译器不会生成执行外错误,它会根据自己的喜好进行优化和重新排序,只要生成的结果符合源代码的要求

但在处理多线程时,这确实可能会导致崩溃,尽管这通常与编译器如何对代码进行重新排序无关(尽管在其他乐观的情况下,这可能会使情况变得更糟)


处理在相同数据上运行的线程时,您需要非常非常小心,并确保您的数据使用适当的保护(信号量/互斥量/原子操作和类似的保护)

问题的关键在于,如果您刚刚开始处理多线程代码(在这里,您明确地谈论线程调度,好像它有点可怕[不是说它不是,而是出于不同的原因])正如其他人所说,如果编译器不能保证正确性,那么它就不会这样做。虽然知道这样的技术存在是很好的,但除非你在编写自己的编译器或做真正的裸机工作,否则它不应该出现问题。

r-无序执行指的是处理器执行管道,而不是编译器本身,正如链接清楚显示的那样。
无序执行是大多数现代CPU管道所采用的一种策略,允许它们动态地重新排序指令,以通常最大限度地减少读/写暂停,这是现代硬件上最常见的瓶颈,因为CPU执行速度和内存延迟之间存在差异(即,与将结果更新回RAM的速度相比,我的处理器获取和处理的速度有多快)。
因此,这主要是硬件功能,而不是编译器功能。
如果您知道您通常使用的是什么,您可以覆盖此功能。Power PC有一个名为EIO(按顺序执行i/o)的奇妙指令,强制CPU刷新所有挂起的读写到内存中-这对于并发编程尤其重要(无论是多线程还是多处理器),因为它确保所有CPU或线程已同步所有内存位置的值。
如果你想深入了解这一点,那么这是一个极好的(尽管很详细)介绍。

HTH

这不是编译器,而是CPU。(事实上两者都是,但CPU更难控制。)不管代码是如何编译的,CPU都会在指令流中向前看,并按顺序执行。通常,例如,由于内存比CPU慢,所以会提前开始读取。(尽早开始,希望在你真正需要之前阅读完毕)

CPU和编译器都基于相同的规则进行优化:只要不影响程序的结果,就对任何内容重新排序*假设为单线程单处理器环境*

所以问题就来了——它优化了单线程,而不是单线程。为什么?因为否则一切都会慢100倍。真的。而且你的大部分代码都是单线程的(即单线程交互)——只有小部分需要以多线程的方式交互

最好/最简单/最安全的控制方法是使用锁-互斥锁、信号量、事件等

只有当你真的,真的,需要优化(基于仔细的测量),然后你才能研究内存障碍和原子操作-这些是用于构建互斥体等的基本指令,正确使用时可以限制无序执行


但在进行此类优化之前,请检查算法和代码流是否正确,以及是否可以进一步减少多线程交互。

让我问一个问题:给定一个程序代码(假设它是单线程应用程序),什么是正确的执行?直观地说,CPU按照代码指定的顺序执行是正确的。这种顺序执行的幻觉是程序员的幻觉

然而,现代CPU并不遵守这种限制。除非违反了依赖关系(数据依赖、控制依赖和内存依赖),否则CPU执行指令的方式是无序的。然而,它对程序员来说是完全隐藏的。程序员永远看不到CPU内部发生了什么

编译器也利用了这一事实。如果可以保留程序的语义(即代码中固有的依赖关系),编译器将对任何可能的指令进行重新排序,以获得更好的性能。一个值得注意的优化是代码提升:编译器可以提升加载指令以最小化内存延迟。但是