作为一个业余爱好项目,我正在努力创建一个带垃圾收集的编程语言。 该语言将被编译为(最好是可移植的)C ++并支持线程。
问题是: 支持两个线程写入"同时"相同(指针大小和对齐)内存位置的不同值。 那么任何线程都可以读取这两个值之间的混合吗?
例如在32位平台上:
线程1写道:AAAAAAAA
线程2写道:BBBBBBBB
任何线程是否总是读取AAAAAAAA或BBBBBBBB,或者他们是否可以读取AAAABBBB或其他一些" mix"两者之间? 我不关心订购,最终成为最终价值。重要的是,不能从该位置读取无效值。
我意识到这可能取决于平台,C ++可能不会为它提供任何承诺。 是否可以保证某些平台,并且是否需要使用内联汇编程序来实现它?
PS:我相信std :: atomic会做出这样的保证,但我认为用于对象引用的所有加载/存储操作都会有很大的开销。
答案 0 :(得分:1)
C ++没有这样的保证,它取决于硬件。 典型的硬件/处理器,例如Arm,x86,amd64,只要写入是32位对齐,那么32位读写操作将是原子的。
一次读取/写入32位一个字节(例如strcpy,memcpy等),所有的赌注都关闭 - 很大程度上取决于这些函数的实现(它们往往会得到很多优化)。
当存在多个内存位置时,在某些平台上会变得更复杂。
说你有
extern int32 a;
extern int32 b;
a = 0x12345678;
b = 0x87654321;
现在,单独地,a和b由线程1原子地写入,但是观察者线程2可以在A之前“看到”B的值变化。
这可能是由硬件和软件引起的。 软件(C ++编译器/优化器)可能会重新排列您的代码,如果它认为它会更好。 (或者,编译器甚至可以避免在某些情况下将值写入a和b)。
硬件还可以在运行时重新安排内存读/写 - 当thread1和thread2在不同的内核上运行时可以看到,并且在core1执行某些操作以使其内部内存管道与系统的其余部分同步之前,core2可能会看到某些内容不同。 Ia64对这些优化非常积极。 X86并不是那么多(因为它会打破我认为的太多遗留代码)。
在C / C ++中,“volatile”基本上可以让你告诉编译器在这个变量周围进行优化时不那么积极 - 尽管它的确实取决于实现。通常意味着编译器不会优化对volatile变量的读/写操作,通常也不会重新安排对它们的访问。
这不会改变处理器在运行时可能会改变的内容。 为此,您需要使用特殊的“内存屏障”内在/操作。 这些细节很复杂,通常隐藏在“原子”之类的东西之后。
哦,此外,大多数系统都有神奇的内存 - 某些地址由硬件保留用于特殊目的。通常,除非您正在编写设备驱动程序,否则不会遇到此问题。