C++ 多线程与并发系统取向(五)—— std::atomic:原子操作与状态一致性(类比 Java Atomic)

一、先问一个核心问题

前面我们已经学了:

  • mutex ------ 保护共享资源
  • condition_variable ------ 线程协作

那为什么还需要:

cpp 复制代码
std::atomic

因为有一种场景:

你不是在保护"复杂资源"

你只是想保证"某个状态是安全的"

二、atomic 解决什么问题?

atomic 解决两件事:

1️⃣ 避免数据竞争

2️⃣ 保证可见性

它适合:

  • 计数器
  • 状态标志
  • 停止信号
  • 简单数值更新

三、先看一个错误示例

cpp 复制代码
bool stop = false;

void worker() {
    while (!stop) {
        // do work
    }
}

主线程:

cpp 复制代码
stop = true;

你以为 worker 会停?

⚠ 可能不会。

原因:

  • 编译器可能优化
  • CPU 缓存可能导致不可见
  • 多核缓存不同步

这叫:

内存可见性问题

四、正确写法:std::atomic

cpp 复制代码
#include <atomic>

std::atomic<bool> stop(false);

void worker() {
    while (!stop.load()) {
        // do work
    }
}

主线程:

cpp 复制代码
stop.store(true);

现在一定安全。

五、Java 类比

Java 写法:

java 复制代码
volatile boolean stop = false;

或者:

java 复制代码
AtomicBoolean stop = new AtomicBoolean(false);

对比:

Java C++
volatile std::atomic
AtomicInteger std::atomic<int>

区别:

C++ 的 atomic 更底层。

六、atomic 的核心特性

1️⃣ 原子性(Atomicity)

操作不会被中断。

例如:

cpp 复制代码
std::atomic<int> counter(0);
counter++;

不会出现:

被其他线程打断。

2️⃣ 可见性(Visibility)

一个线程写入后:

其他线程一定能看到。

3️⃣ 顺序一致性(默认)

默认使用:

cpp 复制代码
memory_order_seq_cst

保证全局一致顺序。

(我们暂时不深挖内存序,只讲工程层)

七、atomic vs mutex 本质区别

对比 atomic mutex
适合 简单变量 复杂资源
是否阻塞 不阻塞 可能阻塞
是否保护容器
是否能替代锁

一句话:

atomic 管"状态"

mutex 管"资源"

八、示例:多线程计数对比

错误版本

cpp 复制代码
int counter = 0;

void task() {
    for (int i = 0; i < 100000; ++i) {
        counter++;
    }
}

结果不稳定。

mutex 版本

cpp 复制代码
std::mutex mtx;
int counter = 0;

void task() {
    for (int i = 0; i < 100000; ++i) {
        std::lock_guard<std::mutex> lock(mtx);
        counter++;
    }
}

正确,但性能差。

atomic 版本

cpp 复制代码
std::atomic<int> counter(0);

void task() {
    for (int i = 0; i < 100000; ++i) {
        counter++;
    }
}

正确,且更高效。

九、重要误区:atomic ≠ 无锁系统

很多人会说:

那我全用 atomic 不就行了?

错误。

atomic 不能保护:

  • vector
  • map
  • queue
  • 多个变量组合状态

例如:

cpp 复制代码
if (x > 0) {
    x--;
}

即使 x 是 atomic,也可能出错。

因为:

判断 + 修改 不是一个原子事务。

十、工程级示例:停止线程

这是最常见使用场景。

cpp 复制代码
#include <thread>
#include <atomic>
#include <iostream>
#include <chrono>

std::atomic<bool> running(true);

void worker() {
    while (running.load()) {
        std::cout << "Working..." << std::endl;
        std::this_thread::sleep_for(std::chrono::milliseconds(500));
    }
    std::cout << "Stopped." << std::endl;
}

int main() {
    std::thread t(worker);

    std::this_thread::sleep_for(std::chrono::seconds(3));
    running.store(false);

    t.join();
}

这就是:

atomic 做状态控制

join 做生命周期管理

十一、atomic 的高级形态(点到为止)

atomic 还支持:

cpp 复制代码
compare_exchange_strong
compare_exchange_weak

用于:

  • 无锁算法
  • CAS 自旋

但:

90% 工程开发用不到。

不要一上来就写 lock-free 队列。

十二、系统取向思维升级

现在你有四层结构:

1️⃣ 线程模型 ------ 谁共享

2️⃣ mutex ------ 保护资源

3️⃣ condition_variable ------ 线程协作

4️⃣ atomic ------ 状态一致性

十三、工程口诀

状态用 atomic

容器用 mutex

等待用 condition_variable

生命周期用 join

十四、和 Java 再对比

Java:

  • synchronized = mutex
  • wait/notify = condition_variable
  • AtomicXXX / volatile = atomic

C++:

  • 更底层
  • 更危险
  • 更灵活

十五、本篇总结一句话

atomic 不是锁的替代品

它是"轻量级状态同步工具"

下一篇(真正组合拳)

第六篇我们做:

工程实战 ------ 用 mutex + cv + atomic 搭一个任务系统骨架

  • 可提交任务
  • 可阻塞等待
  • 可安全关闭
  • 正确释放线程

这是你并发系统能力的第一块工程里程碑。

相关推荐
资深web全栈开发1 小时前
CoI - 组合优于继承:解耦的艺术
android·java·开发语言
低频电磁之道1 小时前
C++中预定义宏
开发语言·c++
工程师0072 小时前
MQTT 概念详解与 C# 实战
开发语言·c#·mqtt通信
fpcc2 小时前
并行编程实战——CUDA编程的Warp Vote
c++·cuda
fpcc2 小时前
并行编程实战——CUDA编程的Warp Shuffle
c++·cuda
代码改善世界2 小时前
栈和队列的实现与详解(C语言版):从底层原理到代码实战
c语言·开发语言
xiaoye-duck2 小时前
《算法题讲解指南:优选算法-滑动窗口》--13 水果成篮
c++·算法
智者知已应修善业2 小时前
【冰雹猜想过程逆序输出】2025-4-19
c语言·c++·经验分享·笔记·算法
wefg12 小时前
【算法】倍增思想(快速幂)
数据结构·c++·算法