atomic
atomic
是什么及相关操作介绍
1. atomic
是什么
在 C++ 中,std::atomic
是 C++11 引入的模板类,位于 <atomic>
头文件中,用于实现线程安全的原子操作。原子操作是指在多线程环境中不可分割的操作,保证操作要么完全完成,要么完全不执行,避免数据竞争(data race)。
- 作用:
- 提供线程安全的变量读写,防止多线程并发访问导致的数据不一致。
- 避免使用锁(如互斥锁),减少锁竞争和上下文切换开销。
- 支持基本数据类型(如
int
、bool
、指针)和其他自定义类型的原子操作。
- 典型场景:
- 多线程计数器。
- 无锁数据结构(如无锁队列)。
- 标志位或状态变量的线程安全更新。
2. std::atomic
的核心特性
- 原子性:操作不可中断,确保线程安全。
- 内存序:通过
std::memory_order
控制内存访问顺序,优化性能和一致性。 - 支持类型:
- 内置类型:
std::atomic<int>
、std::atomic<bool>
等。 - 指针类型:
std::atomic<T*>
。 - 自定义类型:需满足“平凡可复制”条件(Trivially Copyable)。
- 内置类型:
3. 主要操作
以下是 std::atomic
的常用操作,假设 std::atomic<T> var
是一个原子变量:
(1) 基本读写操作
- load:读取原子变量的值。
1
T value = var.load(std::memory_order_acquire);
- 获取
var
的当前值,保证读取最新值。
- 获取
- store:写入新值到原子变量。
1
var.store(new_value, std::memory_order_release);
- 将
new_value
写入var
,确保对其他线程可见。
- 将
- exchange:原子地替换值并返回旧值。
1
T old_value = var.exchange(new_value, std::memory_order_acq_rel);
- 将
var
替换为new_value
,返回替换前的值。
- 将
(2) 比较并交换(CAS)
- compare_exchange_strong:
1
2T expected = current_value;
bool success = var.compare_exchange_strong(expected, desired, std::memory_order_acq_rel);- 如果
var == expected
,则将var
替换为desired
,返回true
。 - 否则,
expected
被更新为var
的当前值,返回false
。 - 适合无锁算法,处理竞争。
- 如果
- compare_exchange_weak:
- 类似
compare_exchange_strong
,但在某些架构上可能因伪失败(spurious failure)返回false
,需配合循环重试。1
2
3
4T expected = current_value;
while (!var.compare_exchange_weak(expected, desired, std::memory_order_acq_rel)) {
// 重试
}
- 类似
(3) 算术和位操作
仅对 std::atomic
的整型或指针类型支持:
- fetch_add / fetch_sub:原子加/减,返回旧值。
1
T old = var.fetch_add(1, std::memory_order_relaxed); // var += 1
- fetch_and / fetch_or / fetch_xor:原子位操作。
1
T old = var.fetch_and(mask, std::memory_order_relaxed); // var &= mask
- **operator++ / operator–**:原子自增/自减。
1
var++; // 等价于 fetch_add(1) + 1
(4) 其他操作
- is_lock_free:检查原子操作是否无锁。
1
bool is_lock_free = var.is_lock_free();
- 返回
true
表示操作由硬件支持,false
表示可能使用内部锁。
- 返回
- wait / notify_one / notify_all(C++20):
- 等待原子变量值变化(类似条件变量)。
1
2var.wait(old_value, std::memory_order_acquire); // 等待 var 变化
var.notify_one(); // 唤醒一个等待线程
- 等待原子变量值变化(类似条件变量)。
4. 内存序(Memory Order)
std::atomic
操作支持指定内存序,控制多线程间的内存访问顺序,平衡正确性和性能:
- **
std::memory_order_relaxed
**:- 仅保证原子性,不保证内存访问顺序。
- 适合独立操作,如计数器。
- **
std::memory_order_acquire
**:- 确保操作后的读取看到其他线程的
release
操作结果。 - 用于消费者线程。
- 确保操作后的读取看到其他线程的
- **
std::memory_order_release
**:- 确保操作前的写入对其他线程的
acquire
操作可见。 - 用于生产者线程。
- 确保操作前的写入对其他线程的
- **
std::memory_order_acq_rel
**:- 结合
acquire
和release
,用于读写操作(如exchange
)。
- 结合
- **
std::memory_order_seq_cst
**(默认):- 提供最强的顺序一致性,保证所有线程看到一致的操作顺序。
- 开销较大,但最安全。
5. 代码示例
以下是一个使用 std::atomic
实现线程安全计数器的例子:
1 |
|
- 说明:多个线程并发调用
fetch_add
,计数器保持线程安全。
6. 注意事项
- 性能:
- 原子操作通常比锁更快,但 CAS 重试可能导致性能波动。
- 选择合适的内存序(如
relaxed
)可优化性能。
- 限制:
- 仅支持简单数据类型或指针,复杂类型需自定义无锁机制。
compare_exchange_weak
可能因伪失败需重试,适合循环场景。
- ABA 问题:
- 在 CAS 操作中,值可能被修改后恢复为原值,导致误判。
- 解决方法:使用版本计数或双字 CAS。
- 适用场景:
- 适合高并发、低冲突场景,如无锁队列、计数器、状态标志。
- 高冲突场景可能仍需锁机制。
7. 总结
- **
std::atomic
**:提供线程安全的原子操作,避免锁开销。 - 核心操作:
load
、store
、exchange
、compare_exchange_strong/weak
、算术/位操作。 - 内存序:通过
memory_order
控制同步和性能。 - 应用:无锁数据结构、线程安全计数器、标志位管理。
All articles on this blog are licensed under CC BY-NC-SA 4.0 unless otherwise stated.
Comments