在ARM架构上,遗憾的是我不确切知道它是什么芯片,是32位int读/写原子?
对基本类型的读/写有什么保证吗?
注意:对于这个问题,我不是在谈论 C 或 C++语言标准。相反,我谈论的是针对特定体系结构的 gcc 编译器实现,因为语言标准对原子性的唯一保证是使用_AtomicC11 或更高版本中的类型或std::atomic<>C++11 或更高版本中的类型。另请参阅我在这个问题底部的更新。
在任何体系结构上,某些数据类型可以原子方式读取和写入,而其他数据类型则需要多个时钟周期,并且可能在操作中间被中断,如果跨线程共享该数据,则会导致损坏。
在8 位单核 AVR 微控制器(例如:Arduino Uno、Nano 或 Mini 使用的 ATmega328 mcu)上,只有8 位数据类型具有原子读取和写入(使用 gcc 编译器和gnu C 或gnu C++语言)。我在不到 2 天的时间里进行了 25 小时的马拉松式调试,然后在这里写下了这个答案。另请参阅此问题的底部以获取更多信息。以及有关使用 AVR-libc 库的 gcc 编译器编译时 AVR 8 位微控制器具有自然原子写入和自然原子读取的 8 位变量的文档。
在(32 位)STM32 单核微控制器上,任何32 位或更小的数据类型绝对自动是原子的(当使用 gcc 编译器和 gnu C 或 gnu C++ 语言编译时,因为ISO C 和 C++ 不保证这一点直到 2011 年版本,_Atomic类型为 C11 …
假设我定义了一个以下的C++对象:
class AClass
{
public:
AClass() : foo(0) {}
uint32_t getFoo() { return foo; }
void changeFoo() { foo = 5; }
private:
uint32_t foo;
} aObject;
Run Code Online (Sandbox Code Playgroud)
该对象由两个线程T1和T2共享.T1经常getFoo()在循环中调用以获得一个数字(如果changeFoo()以前没有被调用,它将始终为0 ).在某些时候,T2调用changeFoo()它来改变它(没有任何线程同步).
是否有任何实际的机会有史以来T1获得的值会有所不同比0或5 的现代计算机体系结构和编译器?到目前为止我调查的所有汇编代码都使用32位内存读写,这似乎可以节省操作的完整性.
其他原始类型呢?
实际意味着您可以提供现有体系结构或符合标准的编译器的示例,其中理论上可以实现此(或具有不同代码的类似情况).我把" 现代"这个词留下了一点主观.
编辑:我可以看到很多人注意到我不应该期待5被读.这对我来说完全没问题,我没有说我这么做(虽然感谢你指出这方面的问题).我的问题更多的是关于上述代码可能发生什么样的数据完整性违规.
我目前正在费力地阅读 ARMv7 内核的 ARM 架构手册。在关于内存访问原子性的章节 A3.5.3 中,它指出:
如果单副本原子加载与单副本原子存储重叠,并且对于任何重叠字节,加载返回由单副本原子存储插入到该字节的一致性顺序中的写入写入的数据,则加载必须返回来自一致性顺序中的某个点的数据不早于由所有重叠字节的单副本原子存储插入一致性顺序的写入。
作为非英语母语人士,我承认我在理解这句话时遇到了一些挑战。
是否存在对内存字节的写入未插入一致性顺序因此上述情况不适用的情况?如果不是,我的说法是否正确,将句子缩短并改写为以下内容:
如果加载恰好返回写入的至少一个字节,则加载必须从不早于写入将它们插入所有重叠字节的一致性顺序的点返回所有重叠字节。
仍然传达相同的含义吗?