C++ 原子操作
外观
介绍[编辑 | 编辑源代码]
原子操作(Atomic Operations)是C++多线程编程中的核心概念,指在单个CPU指令内完成的操作,不会被线程调度机制打断。这类操作用于确保多线程环境下对共享数据的修改是线程安全的,无需显式使用互斥锁(Mutex)。C++11起通过std::atomic
模板类提供原子操作支持。
原子操作适用于以下场景:
- 计数器增减
- 标志位设置
- 无锁数据结构(Lock-Free Data Structures)
为什么需要原子操作?[编辑 | 编辑源代码]
在多线程环境中,多个线程同时修改同一变量可能导致数据竞争(Data Race)。例如,以下非原子操作可能产生不可预测的结果:
#include <iostream>
#include <thread>
int counter = 0;
void increment() {
for (int i = 0; i < 100000; ++i) {
++counter; // 非原子操作
}
}
int main() {
std::thread t1(increment);
std::thread t2(increment);
t1.join();
t2.join();
std::cout << "Counter: " << counter << std::endl; // 结果可能小于200000
}
输出示例(错误结果):
Counter: 153742
原因:++counter
实际包含“读取-修改-写入”三步,线程切换可能导致中间状态丢失。
C++原子类型[编辑 | 编辑源代码]
C++11引入std::atomic<T>
模板类,支持整数、指针和自定义类型(需满足特定条件)。常用原子类型包括:
std::atomic<int>
std::atomic<bool>
std::atomic<size_t>
基本用法[编辑 | 编辑源代码]
修复上述计数问题的原子版本:
#include <atomic>
#include <iostream>
#include <thread>
std::atomic<int> counter(0);
void increment() {
for (int i = 0; i < 100000; ++i) {
++counter; // 原子操作
}
}
int main() {
std::thread t1(increment);
std::thread t2(increment);
t1.join();
t2.join();
std::cout << "Counter: " << counter << std::endl; // 正确输出200000
}
输出:
Counter: 200000
内存顺序(Memory Order)[编辑 | 编辑源代码]
原子操作允许指定内存顺序,控制操作间的可见性。C++提供六种内存顺序,分为三类:
1. 顺序一致(Sequentially Consistent):memory_order_seq_cst
2. 获取-释放(Acquire-Release):memory_order_acquire
, memory_order_release
, memory_order_acq_rel
3. 松散(Relaxed):memory_order_relaxed
示例:使用内存顺序[编辑 | 编辑源代码]
#include <atomic>
#include <thread>
#include <assert.h>
std::atomic<bool> x(false), y(false);
std::atomic<int> z(0);
void write_x_then_y() {
x.store(true, std::memory_order_relaxed);
y.store(true, std::memory_order_release); // 保证之前的操作对acquire可见
}
void read_y_then_x() {
while (!y.load(std::memory_order_acquire)); // 同步release
if (x.load(std::memory_order_relaxed))
++z;
}
int main() {
std::thread a(write_x_then_y);
std::thread b(read_y_then_x);
a.join();
b.join();
assert(z.load() != 0); // 永远不会触发
}
实际应用案例[编辑 | 编辑源代码]
无锁队列[编辑 | 编辑源代码]
原子操作可用于实现高性能无锁队列。以下是一个简化版实现:
#include <atomic>
#include <memory>
template<typename T>
class LockFreeQueue {
private:
struct Node {
std::shared_ptr<T> data;
std::atomic<Node*> next;
Node(T const& value) : data(std::make_shared<T>(value)), next(nullptr) {}
};
std::atomic<Node*> head;
std::atomic<Node*> tail;
public:
void push(T const& value) {
Node* new_node = new Node(value);
Node* old_tail = tail.load();
while (!tail.compare_exchange_weak(old_tail, new_node)) {
old_tail = tail.load();
}
old_tail->next = new_node;
}
std::shared_ptr<T> pop() {
Node* old_head = head.load();
while (old_head && !head.compare_exchange_weak(old_head, old_head->next.load())) {
old_head = head.load();
}
return old_head ? old_head->data : std::shared_ptr<T>();
}
};
性能考虑[编辑 | 编辑源代码]
原子操作通常比互斥锁更快,但在高争用环境下可能因CPU缓存同步(如MESI协议)导致性能下降。建议:
- 对高频访问变量使用
memory_order_relaxed
- 避免在原子操作中包含复杂逻辑
总结[编辑 | 编辑源代码]
- 原子操作通过硬件指令保证操作的不可分割性。
std::atomic
支持整数、指针和自定义类型。- 内存顺序允许开发者平衡性能与一致性需求。
- 适用于计数器、标志位和无锁数据结构等场景。