在C中,“i + = 1;”原子?

In C is “i+=1;” atomic?

在c中,i+=1;是原子的吗?


C标准没有定义它是否是原子的。

在实践中,如果给定的操作是原子操作,则永远不会编写失败的代码,但是如果给定的操作不是原子操作,则很可能编写失败的代码。因此假设不是这样。


不。

C语言标准保证原子的唯一操作是向sig_atomic_t类型的变量分配或检索值,该变量在中定义。

(C99,第7.14章信号处理。)


在C中定义,可能在实践中没有。把它写在汇编中。

标准没有保证。

因此,一个可移植的程序不会做出这样的假设。不清楚您的意思是"必须是原子的",还是"在我的C代码中恰好是原子的",第二个问题的答案是,它取决于很多事情:

  • 并不是所有的机器都有一个递增的内存操作。有些机器需要加载和存储该值,以便对其进行操作,因此答案是"从不"。

  • 在具有增量内存操作的计算机上,不能保证编译器不会输出加载、增量和存储序列,也不能使用其他非原子指令。

  • 在具有增量内存操作的计算机上,相对于其他CPU单元,它可能是原子的,也可能不是原子的。

  • 在具有原子增量内存操作的计算机上,它可能不被指定为体系结构的一部分,而只是CPU芯片的特定版本的属性,甚至只是某些核心逻辑或主板设计的属性。

至于"我如何原子地做到这一点",通常有一种方法可以快速做到这一点,而不是诉诸于(更昂贵的)协商互斥。有时这涉及到特殊的冲突检测可重复的代码序列。最好在汇编语言模块中实现这些,因为它是特定于目标的,所以对HLL没有可移植性好处。

最后,由于不需要(昂贵的)协商互斥的原子操作速度快,因此很有用,而且在任何情况下,对于可移植代码来说,系统通常都有一个库,通常用汇编语言编写,它已经实现了类似的功能。


表达式是否是原子的,只取决于编译器生成的机器代码以及它将运行的CPU体系结构。除非加法可以在一个机器指令中实现,否则它不太可能是原子的。

如果您使用的是Windows,那么您可以使用interlockedincrement()API函数来执行保证的原子增量。有类似的减量函数等。


虽然对于C语言我可能不是原子的,但是应该注意,在大多数平台上,这是原子的。GNU C库文档说明:

In practice, you can assume that int and other integer types no longer than int are atomic. You can also assume that pointer types are atomic; that is very convenient. Both of these assumptions are true on all of the machines that the GNU C library supports and on all POSIX systems we know of.


这真的取决于你的目标和你的UC/处理器的记忆设置。如果我是一个保存在寄存器中的变量,那么它可能是原子的。


问题的答案取决于i是局部变量、static还是全局变量。如果istatic或全局变量,那么不,说明i += 1不是原子变量。但是,如果i是一个局部变量,那么对于在x86体系结构上运行的现代操作系统和其他体系结构来说,该语句是原子的。@丹·克里斯托洛沃对于局部变量的判断是正确的,但也有很多可以说的。

(在下面的内容中,我假设一个现代操作系统,它在x86体系结构上具有保护功能,线程完全通过任务切换实现。)

如果这是C代码,那么语法i += 1意味着i是某种整型变量,如果它是局部变量,则其值存储在诸如%eax之类的寄存器或堆栈中。首先处理简单的情况,如果i的值存储在一个寄存器中,比如%eax中,那么C编译器很可能会将该语句转换为如下内容:

1
addl    $1, %eax

当然,这是原子的,因为没有其他进程/线程能够修改正在运行的线程的%eax寄存器,并且在该指令完成之前,线程本身不能再次修改%eax

如果i的值存储在堆栈中,那么这意味着存在内存提取、增量和提交。比如:

1
2
3
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语句也是原子的。


不,C标准不能保证原子性,而且在实践中,操作不会是原子性的。您必须使用库(如Windows API)或编译器内置函数(gcc、msvc)。


只需在它周围放置一个互斥或信号灯。当然,它不是原子的,您可以用50个左右的线程来创建一个测试程序,访问同一个变量并递增它,以自己检查它。


C/C++语言本身并不要求原子性或缺少原子性。您需要依赖内部函数或库函数来确保原子行为。


通常不会。

如果ivolatile,那么它将取决于您的CPU架构和编译器-如果在主内存中添加两个整数是CPU上的原子,那么C语句可能是volatile int i的原子。


不,不是。如果i的值没有加载到某个寄存器中,就不能在单个汇编指令中完成。