原子操作是并发编程中的一个核心概念。让我详细解释:
什么是原子操作?
原子操作是指在执行过程中不会被中断的操作,要么完全执行,要么完全不执行,不会出现部分执行的状态。
比喻理解
就像原子是不可分割的基本单位一样,原子操作是不可分割的操作单位。
非原子操作的问题
先看一个非原子操作的例子:
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指令实现
- 高性能的并发编程基础
在现代多核处理器系统中,原子操作是实现高效并发程序的重要工具,特别是在实现无锁数据结构、计数器、标志位等场景中。
到此这篇关于c语言原子操作的实现示例的文章就介绍到这了,更多相关c语言 原子操作内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论