1,先上代码
#include <iostream>
#include <omp.h>
int atomicAdd(int* ptr, int value) {
int result;
asm volatile(
"lock xaddl %0, %1\n"
: "=r" (result), "=m" (*ptr)
: "0" (value), "m" (*ptr)
: "memory"
);
return result;
}
int main() {
int sharedValue = 0;
// 原子加法操作
// int increment = 10;
#pragma omp parallel
{
for(int i=0; i<10000; i++)
atomicAdd(&sharedValue, 10);
//atomicAdd(&sharedValue, increment);
}
std::cout << "New value: " << sharedValue << std::endl;
return 0;
}
Makefile:
EXE := hello_atomic
all: $(EXE)
%: %.cpp
g++ -fopenmp $< -o $@
.PHONY: clean
clean:
-rm -rf $(EXE)
2,实验运行效果
18核36线程,每个线程做1W次加法,没有加丢:
3,原理
在 x86 架构的 CPU 中,原子操作通常是通过特殊的 CPU 指令来实现的,这些指令能够确保在多线程并发执行时,线程或进程使用这类指令对共享内存的操作是原子的,即不会被中断或打断。以下是一些常见的 x86 CPU 中用于实现原子操作的指令:
1. XCHG 指令:XCHG 指令用于交换内存中的值和寄存器中的值。在多线程编程中,可以使用 XCHG 指令来实现原子的读-修改-写操作。
2. CMPXCHG 指令:CMPXCHG 指令用于比较首操作数与 XAX的值,如果相等则将第二操作数的值复制给首操作数。这个指令通常用于实现原子的比较并交换操作(Compare and Swap)。
3. LOCK 前缀:LOCK 前缀可以与其他指令一起使用,用于将指令执行过程中的内存访问变为原子操作。例如,LOCK XCHG 或 LOCK CMPXCHG。
4. 内存屏障指令:MFENCE、SFENCE 和 LFENCE 指令用于确保内存操作的顺序性和可见性,以避免乱序执行带来的问题。
这些指令和前缀可以确保在多线程并发执行时,对共享内存的操作是原子的,从而避免竞态条件和数据不一致性问题。在编写并发程序时,可以利用这些原子操作来实现线程安全的共享内存访问。