fetch_add()
是 C++ 中 std::atomic
类型的一个成员函数,用于原子地(atomically)执行加法操作。它在多线程编程中至关重要,能确保数据竞争(data race)不会发生。以下是详细解析:
函数原型
T fetch_add(
T arg,
std::memory_order order = std::memory_order_seq_cst
) noexcept;
-
arg
: 要加上的值(类型与原子对象相同)。 -
order
: 内存顺序(默认为最严格的std::memory_order_seq_cst
)。 -
返回值: 加法操作之前原子变量的值。
核心特性
-
原子性
整个“读取-修改-写入”操作不可分割,不会被线程切换打断。// 线程安全的计数器 std::atomic<int> counter(0); counter.fetch_add(1); // 即使多线程并发,结果也正确
-
返回值是旧值
返回加法发生前的值,类似i++
的行为:std::atomic<int> val(10); int old = val.fetch_add(3); // old=10, val变为13
-
支持类型
-
整数类型(
int
,long
,uint32_t
等) -
指针类型(移动指针偏移)
std::atomic<int*> ptr(arr); // 指向数组arr int* p1 = ptr.fetch_add(2); // p1 = &arr[0], ptr指向&arr[2]
-
内存顺序参数
可选的内存顺序影响操作的同步行为:
内存顺序 | 作用 |
---|---|
std::memory_order_seq_cst | 默认选项,严格顺序一致性,保证所有线程看到的操作顺序一致。 |
std::memory_order_relaxed | 无同步保证,只保证原子性(高性能,但需谨慎使用)。 |
std::memory_order_acq_rel | 加法操作同时具有“获取(acquire)”和“释放(release)”语义。 |
示例:
counter.fetch_add(1, std::memory_order_relaxed); // 高性能计数
与运算符的区别
操作 | 等效调用 | 返回值 |
---|---|---|
val.fetch_add(n) | 直接调用 | 旧值 |
val += n | val.fetch_add(n) + n | 新值 |
val++ / val++ | val.fetch_add(1) | 旧值 |
++val | val.fetch_add(1) + 1 | 新值 |
指针类型的特殊行为
对 atomic<T*>
调用 fetch_add(N)
时,指针会向前移动 N * sizeof(T)
字节:
std::atomic<int*> ptr(&arr[0]);
int* old = ptr.fetch_add(3);
// old = &arr[0]
// ptr 现在指向 &arr[3]
典型应用场景
-
线程安全计数器
std::atomic<int> count(0); void increment() { count.fetch_add(1); // 安全递增 }
-
无锁队列的索引计算
std::atomic<size_t> index(0); void push() { size_t idx = index.fetch_add(1); // 分配槽位 // 使用idx初始化数据 }
-
指针批量分配
struct Item { /* ... */ }; Item pool[100]; std::atomic<Item*> next_free(pool); Item* alloc() { return next_free.fetch_add(1); // 分配一个Item }
注意事项
-
不支持浮点数
std::atomic<float>
/double
没有fetch_add()
,因为浮点运算通常不是原子的。需用互斥锁替代。 -
溢出问题
和普通加法一样,需注意整数溢出(如uint8_t
从 255 加到 0)。 -
内存顺序选择
在保证正确性的前提下,宽松内存序(如relaxed
)可提升性能。
底层实现
-
x86 平台:编译为
LOCK XADD
指令。 -
ARM 平台:使用
LDADD
指令。 -
其他平台:通过 CAS(Compare-And-Swap)循环实现。
示例代码
#include <atomic>
#include <iostream>
#include <thread>
std::atomic<int> counter(0);
void worker() {
for (int i = 0; i < 1000; ++i) {
counter.fetch_add(1, std::memory_order_relaxed);
}
}
int main() {
std::thread t1(worker), t2(worker);
t1.join(); t2.join();
std::cout << counter; // 输出2000(无竞争)
}
💡 提示:在需要获取旧值的场景(如无锁数据结构)使用
fetch_add
;若只需新值,用+=
更简洁。