【C++】原子加法函数fetch_add详解

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)。

  • 返回值: 加法操作之前原子变量的值。


核心特性

  1. 原子性
    整个“读取-修改-写入”操作不可分割,不会被线程切换打断。

    // 线程安全的计数器
    std::atomic<int> counter(0);
    counter.fetch_add(1); // 即使多线程并发,结果也正确
  2. 返回值是旧值
    返回加法发生前的值,类似 i++ 的行为:

    std::atomic<int> val(10);
    int old = val.fetch_add(3); // old=10, val变为13
  3. 支持类型

    • 整数类型(intlonguint32_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 += nval.fetch_add(n) + n新值
val++ / val++val.fetch_add(1)旧值
++valval.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]

典型应用场景

  1. 线程安全计数器

    std::atomic<int> count(0);
    void increment() { 
        count.fetch_add(1); // 安全递增
    }
  2. 无锁队列的索引计算

    std::atomic<size_t> index(0);
    void push() {
        size_t idx = index.fetch_add(1); // 分配槽位
        // 使用idx初始化数据
    }
  3. 指针批量分配

    struct Item { /* ... */ };
    Item pool[100];
    std::atomic<Item*> next_free(pool);
    
    Item* alloc() {
        return next_free.fetch_add(1); // 分配一个Item
    }

注意事项

  1. 不支持浮点数
    std::atomic<float>/double 没有 fetch_add(),因为浮点运算通常不是原子的。需用互斥锁替代。

  2. 溢出问题
    和普通加法一样,需注意整数溢出(如 uint8_t 从 255 加到 0)。

  3. 内存顺序选择
    在保证正确性的前提下,宽松内存序(如 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;若只需新值,用 += 更简洁。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

奇异果冻

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值