一般地,对于int num,num++(或++num),作为读-修改-写操作中,是不是原子.但我经常看到编译器,例如GCC,为它生成以下代码(在这里尝试):
由于第5行对应于num++一条指令,我们可以得出结论,在这种情况下num++ 是原子的吗?
如果是这样,是否意味着如此生成num++可以在并发(多线程)场景中使用而没有任何数据争用的危险(例如,我们不需要制作它,std::atomic<int>并强加相关成本,因为它是无论如何原子)?
UPDATE
请注意,这个问题不是增量是否是原子的(它不是,而且是问题的开头行).它是否可以在特定场景中,即在某些情况下是否可以利用单指令性质来避免lock前缀的开销.而且,作为公认的答案约单处理器的机器,还有部分提到这个答案,在其评论和其他人谈话解释,它可以(尽管不是C或C++).
在我的Linux机器上,sig_atomic_t是一个普通的老int.是否ints具有特殊的原子质量?
$ gcc -v
Using built-in specs.
Target: x86_64-linux-gnu
...
Thread model: posix
gcc version 4.3.2 (Debian 4.3.2-1.1)
$ echo '#include <signal.h>' | gcc -E - | grep atomic
typedef int __sig_atomic_t;
typedef __sig_atomic_t sig_atomic_t;
Run Code Online (Sandbox Code Playgroud) 8.1.2总线锁定
Intel 64和IA-32处理器提供LOCK#信号,该信号在某些关键存储器操作期间自动置位,以锁定系统总线或等效链路.当该输出信号被断言时,来自其他处理器或总线代理的用于控制总线的请求被阻止.软件可以指定在遵循LOCK语义的其他情况下将LOCK前缀添加到指令之前.
它来自英特尔手册,第3卷
听起来内存上的原子操作将直接在内存(RAM)上执行.我很困惑,因为当我分析装配输出时,我看到"没什么特别的".基本上,生成的汇编输出std::atomic<int> X; X.load()只会产生"额外"的影响.但是,它负责正确的内存排序,而不是原子性.如果我理解得X.store(2)恰到好处mov [somewhere], $2.就这样.它似乎没有"跳过"缓存.我知道将对齐(例如int)移动到内存是原子的.但是,我很困惑.
所以,我提出了疑问,但主要问题是:
在Intel的文档称
该指令可以与
LOCK前缀一起使用,以允许指令以原子方式执行.
我的问题是
可以CMPXCHG用内存地址操作吗?从文档看来似乎没有,但任何人都可以确认只能在寄存器中使用实际的VALUE,而不是内存地址吗?
如果CMPXCHG不是原子级和高级语言级CAS必须通过LOCK CMPXCHG(带LOCK前缀)来实现,那么引入这样一条指令的目的是什么?
假设CPU正在运行汇编指令,例如,FOO将在几个时钟(例如10)中执行
中断请求刚好在执行过程中FOO,处理器需要中断.它是否等待命令正确执行,或FOO中止并将重新启动?考虑到不同类型的中断优先级,它的行为是否有所不同?
什么更快:
add DWORD PTR [rbp-0x4],1
Run Code Online (Sandbox Code Playgroud)
或者
mov eax,DWORD PTR [rbp-0x4]
add eax,1
mov DWORD PTR [rbp-0x4],eax
Run Code Online (Sandbox Code Playgroud)
我已经看到编译器生成的第二个代码,所以也许调用add寄存器要快得多?