C++ 学习(9)——多线程(2)--原子操作

原子变量 std::atomic

🌟 目标:理解数据竞争的根源,掌握原子操作的语义,学会使用 std::atomic 来构建无锁线程安全程序。


📚 一、课程目标

  • 理解什么是“原子性”和“数据竞争”
  • 掌握 std::atomic 的使用方式和常见操作
  • 了解原子变量与互斥锁的区别与联系
  • 理解常见原子操作:store()load()fetch_add()

🧠 二、理论内容

1. 什么是原子操作?

  • 原子操作:不可被中断的操作,要么全部完成,要么完全不发生。
  • 多线程环境中,如果多个线程读写同一个变量,且没有同步机制,就会发生数据竞争,导致未定义行为。
  • std::atomic 提供原子性保障,防止数据竞争。

2. 使用 std::atomic 的基本语法

#include <atomic>

std::atomic<int> counter(0);

支持的操作:

counter++;          // 原子自增
counter--;          // 原子自减
counter.store(5);   // 原子写
int x = counter.load(); // 原子读
counter.fetch_add(3); // 原子加法

3. 原子变量替代互斥锁的场景

场景 推荐用法
仅一个整型计数器或标志位 std::atomic
涉及多个变量一致性修改 std::mutex 更合适
大量频繁读写的共享计数 ✅ 原子效率高

4. 原子操作是线程安全的吗?

是的!但:

  • std::atomic 只能保障它自身的线程安全
  • 多个变量之间的同步仍然需要其他手段,比如锁或条件变量。

5. 原子变量的内存序(了解即可)

counter.store(5, std::memory_order_relaxed);
  • 默认使用 memory_order_seq_cst,强顺序;
  • relaxed 可以提升性能,但程序设计更复杂(需谨慎使用)。

🧪 三、实战示例

示例:原子计数器替代互斥锁

#include <iostream>
#include <thread>
#include <atomic>

std::atomic<int> counter(0);

void increase() {
    for (int i = 0; i < 10000; ++i) {
        counter++;
    }
}

int main() {
    std::thread t1(increase);
    std::thread t2(increase);

    t1.join();
    t2.join();

    std::cout << "Final counter: " << counter.load() << std::endl;
    return 0;
}

🔎 输出一定是 20000,无数据竞争!


📋 四、练习题

💡 练习 1:多线程原子加法

  • 编写一个使用 std::atomic<int> 的计数器程序;
  • 启动 4 个线程,每个线程自增 10000 次;
  • 最终输出应为 40000

💡 练习 2:对比互斥锁版本

  • std::mutex 和普通 int 写同样的程序;
  • 对比两者的执行速度(可使用 std::chrono::steady_clock 测量时间);
  • 思考在实际项目中什么时候该选原子,什么时候选锁?

💡 思考题

  • std::atomic<int> 能否完全替代 std::mutex
  • 如果你要对两个变量一起做加法操作,是否还能使用原子?

🧠 小结

知识点 说明
原子性 操作不可被中断
std::atomic 提供无锁的线程安全基本类型
使用时机 单变量读写频繁,且线程竞争不严重
限制 多变量一致性修改需锁

需要我为 Day 6 安排课程吗?或者帮你出练习题答案也可以~

posted @ 2025-07-21 23:09  seekwhale13  阅读(58)  评论(0)    收藏  举报