首页 新闻 会员 周边 捐助

字节赋值是否一定是原子操作

0
悬赏园豆:10 [已解决问题] 解决于 2024-02-03 16:03

单纯好奇, 直接问这个问题可能意义不大,我们可以具体一点。
比如,在C语言中,有如下变量:

volatile uint8_t flag0;
volatile uint8_t flag1;
volatile uint8_t flag2;
volatile uint8_t flag3;

其中flag0与内存地址4字节对齐,0、1、2、3内存连续。

我在某个线程中(为了忽略cache等的影响,暂时讨论分时多线程的单核线程或者中断程序,也欢迎大佬扩展到多核线程的场景进行讨论)进行如下操作:

flag3 = 1;

是否是一个原子操作呢?是否会有影响到其他线程(或中断程序)中对flag0~2的操作(如flag2++)的可能呢?

我认为大部分平台(x86, arm, risv...)应该都是原子操作,不知道是否有特例?

更特别的例子

假设在结构体中,有:

struct xxx {
uint32_t a:1;
uint32_t b:1;
}

我们知道位操作一般是需要读取整个变量的(通常是使用与或操作),请问以上的变量(比如a)赋值时是否有被中断或其他线程打断的可能(应该有吧),打断后程序对另外一个变量(比如b)的赋值 —— 是否会在回到原本的线程后被覆盖导致赋值无效呢

Tamap的主页 Tamap | 菜鸟二级 | 园豆:395
提问于:2024-01-28 01:42
< >
分享
最佳答案
0

你或许没彻底明白原子操作到底是啥。原子操作是对 CPU 执行指令来说的。这都不是原子操作,这简单的赋值语句落实到汇编可不是简单的一句话就搞定的,学了汇编你就应该知道的(甚至受计算机专业培训的人就应该知道这个)。现代 CPU 为了提高执行效率又加入了多核、乱序执行、分支预测等操作,导致连一个汇编语句都不是原子的了,还需要加特殊指令才能保证原子性。乱序执行的特性导致C代码的赋值也不能精确知道什么时候执行,具体看 CPU 平台和编译器。
至于为什么?看官方 CPU 白皮书,不排除有些 CPU 特殊。

收获园豆:10
寂静的羽夏 | 老鸟四级 |园豆:2133 | 2024-01-28 17:05

感谢回答

你或许没彻底明白原子操作到底是啥

是的很抱歉,我其实并不是很了解原子操作这个概念,从工程应用的角度上来说,我了解这个概念的目的仅仅是为了防止多个线程之间对同一个变量的操作出现错误,因此有这些疑惑特此请教。
在这个情景下这个"语句"是否是一个确切的原子操作可能并不只要,主要关心的是在我不'读取"这个字节的前提下,最后对该字节的写入操作反映到总线上就是一次原子操作(总线仲裁应当确保一个内存地址不可能被两个CPU同时写入)

原子操作是对 CPU 执行指令来说的。这都不是原子操作,这简单的赋值语句落实到汇编可不是简单的一句话就搞定的

是的赋值语句可以翻译成很多指令。我想可能有一些我不了解的平台,但假定是ARM平台下,这句C语言对应的汇编语句应当是mov xd, #1。因为平常的学习过程中,老师们描述在32位ARM平台下对于1,2,4字节的变量的赋值,理论上来说是不可被分割的指令。

简而言之我所疑惑的是, 是否有哪个平台,在进行写入操作时,无法单独写入8位数据,而是必须以大于8位的数据量同时写入(比如32位或者总线宽度),那这样是否说明我在写入这8位数据时,与其相邻的与总线宽度对其的变量,其实会被读取并与该数据合起来作为一个更大的变量一起写入呢

Tamap | 园豆:395 (菜鸟二级) | 2024-01-28 17:30

@Tamap: ARM 平台的汇编我不太熟。为了保证赋值的原子性,Intel 是提供了很多汇编指令可以保证,不过对于一般程序员很冷门就是了,甚至是直接往地址写大于数据总线宽度的(打个比方32位的CPU一次性写入64位的数据)也能保证原子性。
说实话,这个得好好看白皮书,ARM 如果有相关指令的话,肯定上面是写的最清楚的,但就是晦涩罢了。

寂静的羽夏 | 园豆:2133 (老鸟四级) | 2024-01-30 12:36
其他回答(1)
0

在C语言中,对于基本数据类型的赋值操作一般是原子操作。但是,这并不代表在多线程或中断情况下不存在问题。在多线程或中断环境下,可能会发生一些意外的情况,即使是原子操作。

单核线程或中断情况: 在单核环境下,赋值操作一般是原子的。但是,即便是原子操作,如果在执行过程中发生了上下文切换(比如线程被中断,另一个线程获得执行权),那么其他线程对共享变量的操作可能会影响到正在进行的原子操作。

在你的例子中,如果一个线程执行 flag3 = 1; 的同时,另一个线程在不同时间点执行 flag2++;,在切换的时候可能导致 flag2 的值被修改,因此可能存在影响。

多核线程情况: 在多核环境下,原子性可能受到缓存一致性的影响。不同核之间的缓存可能需要进行同步,这可能导致一个核上的写操作在另一个核上不可见,从而影响原子性。

对于这种情况,需要使用专门的同步机制,比如使用原子操作函数(atomic functions)或者使用互斥锁(mutex)来确保多线程环境下的原子性。

关于位域的赋值问题,C标准并没有明确规定位域操作的原子性。在多线程或中断环境下,位域的赋值可能会受到相同的问题影响,即使每个位域的赋值本身是原子操作。

为了确保在多线程或中断环境下的正确性,最好使用适当的同步机制,比如使用互斥锁来保护对共享数据的访问。这可以防止在多线程环境下发生竞态条件和数据不一致性的问题。

Technologyforgood | 园豆:7775 (大侠五级) | 2024-01-29 20:22
清除回答草稿
   您需要登录以后才能回答,未注册用户请先注册