C++ 在x86上,bool读/写操作可以不是原子的吗?
假设我们有两个线程,一个在循环中读取bool,另一个可以在特定时间切换它。我个人认为这应该是原子的,因为C++中的代码> siZeof(BoOL)< /C> >是1字节,你不读/写字节,但我想100%肯定。< /P> 那么是还是不是 编辑:C++ 在x86上,bool读/写操作可以不是原子的吗?,c++,x86,boolean,atomic,C++,X86,Boolean,Atomic,假设我们有两个线程,一个在循环中读取bool,另一个可以在特定时间切换它。我个人认为这应该是原子的,因为C++中的代码> siZeof(BoOL)< /C> >是1字节,你不读/写字节,但我想100%肯定。< /P> 那么是还是不是 编辑: 同样,供将来参考,这是否也适用于int?x86仅保证字大小的字对齐读写。它不保证任何其他操作,除非显式原子化。另外,当然,你必须说服你的编译器在第一时间实际执行相关的读写操作。这取决于你所说的“原子”这个词的真正含义 你的意思是“最终值将一次更新”(是的,在
同样,供将来参考,这是否也适用于
int
?x86仅保证字大小的字对齐读写。它不保证任何其他操作,除非显式原子化。另外,当然,你必须说服你的编译器在第一时间实际执行相关的读写操作。这取决于你所说的“原子”这个词的真正含义
你的意思是“最终值将一次更新”(是的,在x86上,字节值和任何至少64位的正确对齐值都是有保证的),还是“如果我将其设置为true(或false),在我设置之后,没有其他线程会读到不同的值”(这不是很确定-你需要一个“锁”前缀以保证) C++11中的“原子”类型解决了三个不同的问题:
使用
std::atomic
可确保正确处理这三个问题。不使用std::atomic
只会让您猜测,充其量只能使用不可移植的代码。底层架构的字数不是既原子又不尽可能低的效率吗?建议它是非原子的。顺便说一句,我不知道标准中有任何要求要求sizeof(bool)
@LightnessRacesinOrbit:5.3.3甚至有关于如何定义实现的说明。是的,sizeof(bool)是实现定义的。我曾经研究过sizeof(bool)==4的体系结构。“如果我将其设置为true(或false),那么在我设置之后,其他线程将不会读取不同的值”。我认为问题很清楚。后一种解释与原子性无关。@jberryman:问题在于缓存以及编译器优化内存读取。b=false在某些线程中,code>不保证所有其他线程在下一个if(b)…
情况下都会检测到b
为false。这要求编译器没有优化对b
到tmp=b;的访问。。。if(tmp).
[其中tmp
是一个寄存器]。根据线程中的代码,编译器会在某些情况下执行此操作。在我设置后,其他线程不会读取不同的值-mfence
或lock
前缀仅用于确定“after”的含义。所有x86系统上的内存都是一致的,因此在store指令最终提交到L1d缓存之后,其他线程都无法读取旧值。您只需要使用屏障来实现seq cst存储,并确保在存储全局可见之前,该线程不会执行任何其他加载。它肯定会很快在全球范围内独树一帜。TL:DR:barrier不会显式刷新或写回缓存,它只会暂停此线程,直到值从存储缓冲区提交到此核心的L1d缓存(从而全局可见)。“如果我将其设置为true(或false),那么在我设置它之后,其他线程将不会读取不同的值”(这不是很确定-您需要一个“锁”前缀以保证)——bool可以有任何硬件实现,另一个线程可以读取任何,甚至可能是bool的“部分”状态。但读取的值被解释为真或假。所以不能有任何“不同的价值”。从这个意义上说,阅读布尔总是“原子的”——我们总是得到正确或错误,从来没有什么不同。锁定只需要在rmw操作的情况下,或者当我们需要在这个bool和其他mem之间排序时,难道在运行时不存在CPU指令(或内存访问)重新排序吗?编译器可以对加载和存储进行重新排序,但CPU也可以这样做。@RomanKruglov:在x86上,只有StoreLoad重新排序是可能的(),因此除了阻止编译时重新排序之外,只有seq cst存储需要额外的排序。(例如,mov
+mfence
,或者更好的xchg
来实现seq-cst存储。)一般来说,在其他ISA上,是的负载、存储和RMW可能需要额外的屏障,如果它们没有使用mou-released
。缓存一致性不是问题;正常系统已经一致(使用MESI或变体)。原子的
实际上需要做的是阻止编译器在寄存器中保留值,而寄存器是线程私有的。(). 另外,对于x86上的seq cst存储,在以后的加载可以运行之前,暂停当前线程,直到存储变得全局可见(例如,通过使用xchg
或mfence
)。全局可见性将自行发生,但可能在以后加载之后发生。另请参阅,另请参阅:手动一致性。C++是围绕着共享共享内存的假设而设计的,所以你需要做的就是确保存储或负载实际上在ASM中发生,而不是在登记器中保持一个值。在一个假设非相干共享内存的机器上,每一个同步都必须刷新所有的(或者需要很多跟踪),但是我不知道任何非相干共享线程的标准线程的C++实现。