每日一个C++知识点|原子操作

上一篇文章讲了C++多线程的基础知识, 今天我们来讲讲原子操作, 原子操作也是C++多线程的主要内容

什么是原子操作

什么是原子操作呢? 就是一个操作执行像原子一样不可再分割, 在多线程环境中就不会被其他线程打断, 因而就会保证某个操作执行的连续性和完整性

也就是说,一个操作要么全部执行完毕,要么完全不执行

如果没有原子操作

如果没有原子操作的话, 在多线程环境中会导致数据读写混乱, 下面让我们用代码举个经典的反例看看

cpp 复制代码
#include <iostream>
#include <thread>
#include <vector>

int count = 0; // 普通整型变量

// 线程执行的函数:count自增1000次
void increment() {
    for (int i = 0; i < 1000; ++i) {
        count++; // 看似简单的自增,实则暗藏危机
    }
}

int main() {
    std::vector<std::thread> threads;

    // 创建10个线程
    for (int i = 0; i < 10; ++i) {
        threads.emplace_back(increment);
    }

    // 等待所有线程执行完毕
    for (auto& t : threads) {
        t.join();
    }

    std::cout << "最终count值:" << count << std::endl; // 结果远小于10000

    return 0;
}

上述代码的基本内容是让10个线程各自给count加1000次,但实际运行成果总小于1000

是什么原因呢?

因为count++这个操作,在CPU层面被拆成三步:

  1. 从内存中读取count的值存到寄存器
  2. 寄存器中的值+1
  3. 把新值写回内存

因为这三步不是"不可分割的",所以容易造成读写混乱,线程A刚读完count,还没有在寄存器+1,线程B就插进来读了同一个值,然后各自+1写回,相当于白加了一次,导致数据被覆盖, 这就显得原子操作是多么重要了, 下面让我们来看看原子操作是怎么使用的~

原子操作的使用

核心工具

std::atomic:它是 C++11 开始提供的原子类型模板,不需要手动加锁,也可以实现线程安全

常用方法

以int类型变量count为例

  1. 读值count.load()
  2. 写值count.store(10)
  3. 自增count.fetch_add(1)
  4. 交换count.exchange(20)

下面让我们用原子操作来修复上述的例子

cpp 复制代码
#include <iostream>
#include <thread>
#include <vector>
#include <atomic> // 包含原子操作的头文件

std::atomic<int> count = 0; // 原子整型变量

// 线程执行的函数:count自增1000次
void increment() {
    for (int i = 0; i < 1000; ++i) {
        // 方式1:使用fetch_add原子自增
        count.fetch_add(1);
        // 方式2:直接用count++(重载后的原子操作,效果一样)
        // count++;
    }
}

int main() {
    std::vector<std::thread> threads;

    // 创建10个线程
    for (int i = 0; i < 10; ++i) {
        threads.emplace_back(increment);
    }

    // 等待所有线程执行完毕
    for (auto& t : threads) {
        t.join();
    }

    // 原子变量可以直接输出,底层会调用load()
    std::cout << "最终count值:" << count << std::endl; // 结果一定是10000

    return 0;
}

运行结果如下: 这次运行后,count的值10000,使用了原子操作后不会被其他线程打断

这就是原子操作在多线程场景中的使用意义

总结

  1. 原子操作的核心是不可分割,能避免多线程下的数椐竞争
  2. 原子操作通过std::atomic实现

那么我们在上一篇多线程基础的文章中讲到了互斥锁,也可以解决数据竞态条件的问题,那么原子操作和互斥锁又有什么区别和联系呢?他们谁的性能更强呢?如果点赞量超过1,我下期将会重点讲讲原子操作和互斥锁之间的关系~

希望这篇文章能帮你搞懂 C++ 原子操作!如果觉得有用,别忘了点赞哟

或者关注本人,我将会持续分享C++的内容~

相关推荐
单片机设计·······20 分钟前
【dz-1162】汽车前玻璃雨刮器设计
c++·单片机·嵌入式硬件
AI智讯中枢39 分钟前
高性能 C++ 实战 (五):perf+FlameGraph 火焰图生产实战,精准定位 CPU / 缓存 / 锁瓶颈,避坑 + 完整实操案例
linux·c++·性能调优·性能分析·perf·flamegraph·火焰图
繁华的地方不一定留下你的脚印44 分钟前
C++ 算法与 ranges:用 find_if、transform 写清数据处理意图
开发语言·c++·算法
张小姐的猫1 小时前
【AI大模型接入SDK】 —— 前端页面 & 项目总结与拓展
前端·数据结构·数据库·c++·人工智能·chatgpt
AC赳赳老秦1 小时前
财报附注表格精准提取:OpenClaw 从 PDF 年报附注挖掘隐藏明细,补齐财务分析维度
java·汇编·c++·python·青少年编程·deepseek·openclaw
欧特克_Glodon1 小时前
OpenCV计算机视觉开发入门与实践(基于C++):专栏内容介绍及目录
c++·人工智能·opencv·计算机视觉
码匠许师傅2 小时前
【C++三方组件】Asio 下篇:C++20 协程版 TCP 客户端与服务端
c++·tcp/ip·c++20
一只旭宝2 小时前
【C++复习】四种类型转换 + 常见设计模式 + Redis/MySQL(后端面试复习完整版)
c++·redis·笔记·mysql·设计模式
rhett. li2 小时前
用 AI 写 C++ 桌面 UI:TRAE + nim_duilib 实战指南
c++·人工智能·ui
ShineWinsu3 小时前
对于Redis:Redis特性以及应用场景的解析
linux·c++·redis·缓存·高并发·分布式系统·key-value