在C语言中,赋值运算符=和减法赋值运算符-=是否为原子操作?

4
int b = 1000;
b -= 20;

以上内容中是否有原子操作?在C语言中,什么是原子操作?


2
C语言不支持线程,更别提原子操作了。 - David Heffernan
3
@DavidHeffernan 那就是完全错误的。sig_atomic_t自C89(!)以来就存在了。 - Jens
@Jens,sig_atomic_t是存在的,但它如何允许您以原子方式对变量执行算术运算?我认为这个问题涉及线程而不是信号处理程序。 - David Heffernan
@Jens 请您定义一下"原子操作"的意思?在哪些情况下,操作是否是原子的会有影响?问题和标题中也没有提到信号处理程序。而在信号处理程序的上下文中,"原子"并不等同于线程的"原子"。所以,如果我们不知道"原子"的含义,我们怎么能说出任何有用的话呢? - David Heffernan
关于 = 和 C++:https://dev59.com/eGsy5IYBdhLWcg3w0RbT - Ciro Santilli OurBigBook.com
显示剩余4条评论
5个回答

9

这取决于具体的实现。按照标准,在C语言中没有任何一个操作是原子性的。如果您需要原子操作,可以查看您编译器的内置函数。


C1X将支持原子类型。 - Anthony Williams
2
这完全是错误的。自C89以来就支持sig_atomic_t了! - Jens
@Jens sig_atomic_t在信号处理程序方面保证是原子的,但在一般情况下,例如在多线程进程中,并不保证是原子的。 - Marcus

4

这取决于架构/实现。

如果您想要原子操作,我认为C99通过sig_atomic_t类型进行了标准化,但不确定。

来自GNU LibC文档:

实际上,您可以假设int和其他不超过int的整数类型是原子的。 您还可以假定指针类型是原子的; 这非常方便。 所有GNU C库支持的机器和我们所知道的所有POSIX系统上都是如此。


1
相对于线程而言,信号的原子性是非常不同的。 - R.. GitHub STOP HELPING ICE
1
@hexa 你不是在说 b -= 20 是线程安全的吧? - David Heffernan
1
@DavidHeffernan 他还问到 int b = 1000 - Vinicius Kamakura
1
@andrewdski:考虑实现。要使某个操作在信号方面是原子性的,必须确保信号不会在操作中被接收。要使某个操作在线程方面是原子性的,必须确保没有其他线程在操作中看到该对象。接收信号(特别是如果您只担心由此线程接收的信号的原子性)与另一个线程执行根本不同。现在,正如GNU使用硬件原子类型来实现sig_atomic_t一样,因此您可以同时获得两者,但是除了不是Unix之外,GNU还不是C。 - Steve Jessop
1
一个 C 实现 可以 通过在所有访问周围禁用信号并使用秘密内部锁或标志来合法地实现 sig_atomic_t,但不使用原子 CPU 指令,并且(假设 C 实现具有线程)在访问期间不禁用多线程。这很不可能,而且可能效率极低,但是合法的,因此展示了关于信号与线程的原子 wrt 定义的差异。 - Steve Jessop
显示剩余5条评论

2
这个链接似乎是在正确地告诉我们C语言中原子操作的定义:http://odetocode.com/blogs/scott/archive/2006/05/17/atomic-operations.aspx。它说,“……计算机科学采用术语‘原子操作’来描述一条指令,该指令不可分割,也不能被其他执行线程打断。”根据这个定义,在原始问题的第一行代码中,使用了一个非原子操作,因为它可能会在执行过程中被其他线程打断。
int b=1000;
b-=20;

第一行代码应该是一个原子操作。如果CPU的指令集中包含直接从内存中减去的指令,则第二个代码行可以是原子操作。我认为这样做的原因是,第一行代码很可能只需要一个汇编(机器)指令。指令要么执行要么不执行。我认为任何机器指令都不能在中途被打断。

在同一个链接中,它说:“如果线程A将32位值作为原子操作写入内存,线程B将永远无法读取内存位置并仅查看写出的前16位。” 看起来任何单个机器指令都不能在中途被打断,因此在线程之间自动成为原子操作。


你提到的链接是关于C#的讨论。 - undefined

1

在C语言中,增加和减少一个数字不是原子操作。某些架构支持原子增加和减少指令,但不能保证编译器会使用它们。你可以看看Qt的引用计数作为例子。它使用原子引用计数,在某些平台上,它是通过特定于平台的汇编代码实现的,而在其他平台上,则使用互斥锁来锁定计数器。

如果您在代码的性能关键部分没有进行增加或减少操作,那么您只需在执行此操作时使用互斥锁即可。如果您在代码的性能关键部分使用它,您可能需要尝试以不使用共享内存的方式重写代码,以便从多个位置访问此操作,或者使用具有更高粒度的互斥锁,以便它们不会影响性能,或者使用汇编来确保操作是原子的。


矛盾修辞法。我想了解何时使用原子操作,何时使用互斥量、信号量等,这三者之间有什么区别? - Registered User

-1

引用自ISO C89,7.7信号处理<signal.h>

定义的类型是sig_atomic_t,它是一个整数类型的对象,可以作为原子实体访问,即使存在异步中断也是如此。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接