C语言中原子操作(简要)
原子操作是并发编程中的一个核心概念。让我详细解释:
什么是原子操作?
原子操作是指在执行过程中不会被中断的操作,要么完全执行,要么完全不执行,不会出现部分执行的状态。
比喻理解
就像原子是不可分割的基本单位一样,原子操作是不可分割的操作单位。
非原子操作的问题
先看一个非原子操作的例子:
int counter = 0;// 这不是原子操作!
void increment() {counter = counter + 1;
}
在汇编层面,这通常分为三步:
mov eax, [counter] ; 1. 读取counter到寄存器
inc eax ; 2. 寄存器值加1
mov [counter], eax ; 3. 写回内存
竞态条件问题:
线程A:读取counter=0
线程B:读取counter=0
线程A:计算0+1=1
线程B:计算0+1=1
线程A:写入counter=1
线程B:写入counter=1 ← 结果应该是2,但实际是1!
C语言中的原子操作
C11标准引入的原子类型
#include <stdatomic.h>// 声明原子变量
atomic_int atomic_counter = ATOMIC_VAR_INIT(0);// 原子操作
void increment_atomic() {atomic_fetch_add(&atomic_counter, 1); // 原子加法
}int main() {atomic_counter = 5; // 原子存储int value = atomic_counter; // 原子加载printf("Counter: %d\n", value);return 0;
}
常见的原子操作函数
基本操作
#include <stdatomic.h>atomic_int counter = ATOMIC_VAR_INIT(0);// 加载和存储
int load_value = atomic_load(&counter); // 原子读取
atomic_store(&counter, 42); // 原子写入// 交换操作
int old_value = atomic_exchange(&counter, 100); // 原子交换// 比较交换(CAS - Compare And Swap)
int expected = 100;
if (atomic_compare_exchange_strong(&counter, &expected, 200)) {printf("CAS成功: 旧值=%d, 新值=200\n", expected);
}
算术运算
// 原子加法
int old = atomic_fetch_add(&counter, 5); // counter += 5,返回旧值// 原子减法
atomic_fetch_sub(&counter, 3); // counter -= 3// 原子自增/自减
atomic_fetch_add(&counter, 1); // counter++
atomic_fetch_sub(&counter, 1); // counter--
位运算
atomic_int flags = ATOMIC_VAR_INIT(0);// 原子位操作
atomic_fetch_or(&flags, 0x01); // flags |= 0x01
atomic_fetch_and(&flags, ~0x01); // flags &= ~0x01
atomic_fetch_xor(&flags, 0x03); // flags ^= 0x03
原子操作的实际例子
1. 无锁计数器
#include <stdatomic.h>
#include <pthread.h>
#include <stdio.h>atomic_int counter = ATOMIC_VAR_INIT(0);void* worker(void* arg) {for (int i = 0; i < 100000; i++) {atomic_fetch_add(&counter, 1); // 原子自增}return NULL;
}int main() {pthread_t t1, t2;pthread_create(&t1, NULL, worker, NULL);pthread_create(&t2, NULL, worker, NULL);pthread_join(t1, NULL);pthread_join(t2, NULL);printf("最终计数: %d (应该是200000)\n", atomic_load(&counter));return 0;
}
2. 自旋锁实现
#include <stdatomic.h>typedef atomic_flag spinlock_t;void spinlock_init(spinlock_t* lock) {atomic_flag_clear(lock);
}void spinlock_lock(spinlock_t* lock) {// 忙等待,直到获得锁while (atomic_flag_test_and_set(lock)) {// 可选的:减少CPU占用// __builtin_ia32_pause(); // x86的PAUSE指令}
}void spinlock_unlock(spinlock_t* lock) {atomic_flag_clear(lock);
}
3. 无锁栈(Lock-Free Stack)
#include <stdatomic.h>
#include <stdlib.h>typedef struct Node {int data;struct Node* next;
} Node;typedef struct {_Atomic(Node*) top;
} LockFreeStack;void stack_init(LockFreeStack* stack) {atomic_store(&stack->top, NULL);
}void stack_push(LockFreeStack* stack, int value) {Node* new_node = malloc(sizeof(Node));new_node->data = value;Node* old_top;do {old_top = atomic_load(&stack->top);new_node->next = old_top;} while (!atomic_compare_exchange_weak(&stack->top, &old_top, new_node));
}int stack_pop(LockFreeStack* stack) {Node* old_top;Node* new_top;do {old_top = atomic_load(&stack->top);if (old_top == NULL) return -1; // 栈空new_top = old_top->next;} while (!atomic_compare_exchange_weak(&stack->top, &old_top, new_top));int value = old_top->data;free(old_top);return value;
}
内存顺序(Memory Order)
原子操作还涉及内存可见性问题:
#include <stdatomic.h>atomic_int data = ATOMIC_VAR_INIT(0);
atomic_int flag = ATOMIC_VAR_INIT(0);// 生产者线程
void producer() {atomic_store_explicit(&data, 42, memory_order_relaxed);atomic_store_explicit(&flag, 1, memory_order_release); // 释放语义
}// 消费者线程
void consumer() {while (atomic_load_explicit(&flag, memory_order_acquire) == 0) {// 等待}int value = atomic_load_explicit(&data, memory_order_relaxed);printf("Data: %d\n", value); // 保证看到42
}
不同平台的原生原子操作
x86架构
// 内联汇编实现原子操作
int atomic_increment(int* value) {__asm__ __volatile__("lock incl %0" // lock前缀确保原子性: "+m" (*value):: "cc");return *value;
}
GCC内置原子操作
int counter = 0;// GCC内置的原子操作
void increment_gcc() {__sync_fetch_and_add(&counter, 1);
}int compare_and_swap_gcc(int* ptr, int oldval, int newval) {return __sync_val_compare_and_swap(ptr, oldval, newval);
}
原子操作的优缺点
优点:
高性能:避免锁的开销
无死锁:不会出现锁顺序问题
可扩展性:在多核系统中表现良好
缺点:
复杂性:正确实现很困难
ABA问题:在CAS操作中可能出现
平台依赖性:不同硬件支持程度不同
原子操作 vs 互斥锁
特性 | 原子操作 | 互斥锁 |
---|---|---|
性能 | 高(硬件支持) | 中等(系统调用) |
复杂度 | 高 | 低 |
适用场景 | 简单操作(计数器、标志位) | 复杂临界区 |
死锁风险 | 无 | 有 |
可扩展性 | 好 | 一般 |
总结
原子操作是:
不可分割的操作单位
线程安全的,无需额外同步
硬件支持的,通常通过CPU指令实现
高性能的并发编程基础
在现代多核处理器系统中,原子操作是实现高效并发程序的重要工具,特别是在实现无锁数据结构、计数器、标志位等场景中。