C语言原子量的使用
在C语言中,原子量(Atomic)用于实现多线程环境下的无锁同步操作,确保对共享变量的操作是不可分割的(即原子性)。C11标准引入了 <stdatomic.h>
头文件,提供了对原子操作的支持。
互斥量用于保护多行代码块,原子量可以保护一行代码(对原子变量的操作)。以下是原子量的核心用法和注意事项:
1. 原子类型声明
使用 _Atomic
修饰符声明原子变量:
#include <stdatomic.h>
_Atomic int counter = ATOMIC_VAR_INIT(0); // 声明并初始化为0
或简写:
atomic_int counter = 0; // 等效于 _Atomic int
2. 基本原子操作
加载(Load)和存储(Store)
int value;
value = atomic_load(&counter); // 原子读取
atomic_store(&counter, 42); // 原子写入
交换(Exchange)
int old = atomic_exchange(&counter, 100); // 将counter设为100,返回旧值
比较并交换(CAS, Compare-And-Swap)
int expected = 10;
int desired = 20;
if (atomic_compare_exchange_strong(&counter, &expected, desired)) {
// 成功:counter原值为expected,被更新为desired
} else {
// 失败:counter当前值 != expected,expected被更新为实际值
}
3. 原子算术/位运算
atomic_fetch_add(&counter, 5); // 原子加5,返回旧值
atomic_fetch_sub(&counter, 3); // 原子减3
atomic_fetch_or(&counter, 0x1); // 原子按位或
atomic_fetch_and(&counter, ~0x1); // 原子按位与
4. 内存顺序(Memory Order)
指定原子操作的内存同步行为,控制指令重排和可见性:
atomic_store_explicit(&counter, 42, memory_order_release); // 写入释放
int val = atomic_load_explicit(&counter, memory_order_acquire); // 读取获取
常用选项:
• memory_order_relaxed
:仅保证原子性,无同步/顺序约束。
• memory_order_acquire
:本操作前的所有读写不会被重排到它之后。
• memory_order_release
:本操作后的所有读写不会被重排到它之前。
• memory_order_seq_cst
:严格顺序一致性(默认)。
5. 示例:线程安全的计数器
#include <stdatomic.h>
#include <stdio.h>
#include <threads.h>
atomic_int counter = 0;
int thread_func(void *arg) {
for (int i = 0; i < 10000; i++) {
atomic_fetch_add(&counter, 1);
}
return 0;
}
int main() {
thrd_t t1, t2;
thrd_create(&t1, thread_func, NULL);
thrd_create(&t2, thread_func, NULL);
thrd_join(t1, NULL);
thrd_join(t2, NULL);
printf("Counter: %d\n", counter); // 正确输出20000
return 0;
}
6. 注意事项
- 性能:原子操作比普通操作慢,仅在必要时使用。
- 适用场景:简单共享变量(如标志、计数器),复杂逻辑仍需互斥锁。
- 兼容性:需C11及以上编译器(如GCC
-std=c11
)。 - 避免ABA问题:CAS操作需注意值被其他线程多次修改的情况。
7. 扩展知识
• 无锁编程:原子量是实现无锁数据结构的基础(如队列、栈)。
• 编译器屏障:atomic_signal_fence()
用于线程与信号处理函数间的同步。
通过合理使用原子量,可以高效解决多线程竞争问题,但需谨慎设计以避免逻辑错误。