在C中,是i+=1;
原子?
答案 0 :(得分:93)
C标准没有定义它是否是原子的。
实际上,如果给定的操作 原子,你永远不会编写失败的代码,但如果不是,你可能会编写失败的代码。所以假设它不是。
答案 1 :(得分:20)
没有
C语言标准保证唯一的原子操作是在sig_atomic_t
中定义的<signal.h>
类型的变量中分配或检索值。
(C99,第7.14章信号处理。)
答案 2 :(得分:13)
标准不做任何保证。
因此便携式程序不会做出假设。目前尚不清楚你的意思是“必须是原子的”,还是“在我的C代码中恰好是原子的”,第二个问题的答案是它取决于很多事情:
并非所有机器都有增量内存op。有些人需要加载和存储值才能对其进行操作,所以答案就是“从不”。
在具有增量内存op的机器上,无法保证编译器无论如何都不会输出加载,递增和存储序列,或者使用其他非原子指令。
< / LI>在具有增量内存操作的计算机上,相对于其他CPU单元,它可能是原子的,也可能不是原子的。
在具有原子增量内存操作的机器上,它可能不会被指定为体系结构的一部分,而只是CPU芯片的特定版本的属性,甚至只是某些核心逻辑或主板的属性设计。
关于“我如何以原子方式做这件事”,通常有一种方法可以快速完成,而不是诉诸(更昂贵)协商互斥。有时,这涉及特殊的碰撞检测可重复代码序列。最好在汇编语言模块中实现这些,因为它无论如何都是特定于目标的,因此HLL没有可移植性优势。
最后,因为不需要(昂贵的)协商互斥的原子操作是快速且因此有用的,并且在任何情况下需要便携式代码,系统通常具有通常以汇编语言编写的库,其已经实现了类似的功能。
答案 3 :(得分:4)
表达式是否是原子的只取决于编译器生成的机器代码,以及它将运行的CPU架构。除非可以在一个机器指令中实现添加,否则它不太可能是原子的。
如果您使用的是Windows,则可以使用InterlockedIncrement() API函数来保证原子增量。递减similar functions等等。
答案 4 :(得分:2)
这实际上取决于您的目标和您的uC /处理器的助记符集。 如果i是寄存器中的变量,则可以使其成为原子。
答案 5 :(得分:2)
虽然我可能不是C语言的原子,但应该注意在大多数平台上都是原子的。 GNU C Library documentation州:
实际上,您可以假设int和其他不超过int的整数类型都是原子的。您还可以假设指针类型是原子的;这很方便。这两个假设都适用于GNU C库支持的所有机器以及我们所知道的所有POSIX系统。
答案 6 :(得分:1)
不,不是。如果i的值没有加载到其中一个寄存器,则不能在一个汇编指令中完成。
答案 7 :(得分:1)
通常不会。
如果i
是volatile
,那么它将取决于您的CPU架构和编译器 - 如果在主内存中添加两个整数在您的CPU上是原子的,那么C语句可能是原子的volatile int i
。
答案 8 :(得分:1)
C / C ++语言本身并未声明原子性或缺乏原子性。您需要依赖内在函数或库函数来确保原子行为。
答案 9 :(得分:1)
只需在其周围放置互斥锁或信号量即可。当然它不是原子的,你可以制作一个测试程序,有50个左右的线程访问同一个变量并递增它,自己检查
答案 10 :(得分:1)
不,C标准不保证原子性,实际上,操作不是原子的。您必须使用库(例如Windows API)或编译器内置函数(GCC,MSVC)。
答案 11 :(得分:1)
您的问题的答案取决于i
是本地变量,static
还是全局变量。如果i
是static
或全局变量,那么不,语句i += 1
不是原子的。但是,如果i
是一个局部变量,则对于在x86体系结构上运行的现代操作系统,语句是原子,也可能是其他体系结构。 @Dan Cristoloveanu在本地变量案例的正确轨道上,但也可以说更多。
(在下文中,我假设一个现代操作系统在x86架构上提供保护,线程完全由task switching实现。)
鉴于这是C代码,语法i += 1
意味着i
是某种整数变量,如果它是局部变量,则其值存储在这样的寄存器中作为%eax
或在堆栈中。首先处理easy case,如果i
的值存储在寄存器中,比如说%eax
,那么C编译器很可能会将语句转换为类似:
addl $1, %eax
当然是原子的,因为没有其他进程/线程能够修改正在运行的线程的%eax
寄存器,并且在该指令完成之前,线程本身不能再次修改%eax
。
如果i
的值存储在堆栈中,则表示存在内存提取,增量和提交。类似的东西:
movl -16(%esp), %eax
addl $1, %eax
movl %eax, -16(%esp) # this is the commit. It may actually come later if `i += 1` is part of a series of calculations involving `i`.
通常这一系列操作不是原子的。但是,在现代操作系统上,进程/线程不应该能够修改另一个线程的堆栈,因此这些操作 完成,而其他进程无法干预。因此,在这种情况下,语句i += 1
也是原子的。