当前位置: 首页 > news >正文

C++ 原子操作完全指南 + 单例模式实战

C++ 原子操作完全指南 + 单例模式实战

一、std::atomic 基础

1.1 基本用法

std::atomic保证对变量的读写是不可中断的(线程安全),无需显式加锁。

#include<atomic>#include<thread>#include<iostream>std::atomic<int>counter{0};voidincrement(){for(inti=0;i<100000;++i){counter++;// 原子自增,线程安全}}intmain(){std::threadt1(increment);std::threadt2(increment);t1.join();t2.join();std::cout<<counter<<"\n";// 保证输出 200000}

1.2 核心 API

操作说明
load()原子读
store(val)原子写
exchange(val)原子交换,返回旧值
compare_exchange_weak(expected, desired)CAS,原子比较并交换
fetch_add(val)/fetch_sub(val)原子加减,返回旧值
++/--重载运算符,等价于fetch_add(1)
std::atomic<int>a{10};intx=a.load();// x = 10a.store(20);// a = 20intold=a.exchange(30);// old = 20, a = 30intexpected=30;boolok=a.compare_exchange_weak(expected,40);// 如果 a == 30,则 a = 40,返回 true// 如果 a != 30,则 expected = a 的当前值,返回 false

二、内存序 (Memory Order)

std::atomic默认使用memory_order_seq_cst(顺序一致性),性能开销最大。可以显式指定更宽松的内存序。

2.1 五种内存序

宽松程度 seq_cst < acquire/release < acq_rel < relaxed (最强) (最弱,性能最好)
内存序含义适用场景
memory_order_relaxed只保证原子性,不保证顺序纯计数器(如引用计数)
memory_order_acquire操作:之后的读写不会被重排到这次读之前获取锁、读取共享数据
memory_order_release操作:之前的读写不会被重排到这次写之后释放锁、发布共享数据
memory_order_acq_rel同时包含 acquire + release读-修改-写操作(如 CAS)
memory_order_seq_cst全局顺序一致性(默认)简单但最慢

2.2 Acquire-Release 同步示例

#include<atomic>#include<thread>std::atomic<int>data{0};std::atomic<bool>ready{false};voidproducer(){data.store(42,std::memory_order_relaxed);// ① 写数据ready.store(true,std::memory_order_release);// ② 发布信号}voidconsumer(){while(!ready.load(std::memory_order_acquire))// ③ 等待信号;// ④ 保证能看到 ① 的写入std::cout<<data.load(std::memory_order_relaxed)<<"\n";// 一定输出 42}intmain(){std::threadt1(producer);std::threadt2(consumer);t1.join();t2.join();}

关键release写和acquire读配对,形成一个同步点。保证在release之前的所有写操作,对acquire之后的读操作可见。


三、自旋锁实现(CAS 实战)

#include<atomic>#include<thread>classSpinLock{std::atomic_flag flag=ATOMIC_FLAG_INIT;public:voidlock(){// test_and_set: 原子地设置 flag 为 true,返回旧值while(flag.test_and_set(std::memory_order_acquire)){// 自旋等待}}voidunlock(){flag.clear(std::memory_order_release);}};SpinLock spinlock;intshared=0;voidwork(){for(inti=0;i<10000;++i){spinlock.lock();++shared;// 临界区spinlock.unlock();}}

C++20 起std::atomic_flagtest()方法可以只读不修改。


四、无锁队列(简单示例)

template<typenameT>classLockFreeStack{structNode{T data;Node*next;};std::atomic<Node*>head{nullptr};public:voidpush(constT&value){Node*new_node=newNode{value,nullptr};// CAS 循环:如果 head 没变,就更新为 new_nodedo{new_node->next=head.load(std::memory_order_relaxed);}while(!head.compare_exchange_weak(new_node->next,// expectednew_node,// desiredstd::memory_order_release,std::memory_order_relaxed));}boolpop(T&result){Node*old_head=head.load(std::memory_order_acquire);while(old_head!=nullptr){if(head.compare_exchange_weak(old_head,old_head->next,std::memory_order_release,std::memory_order_relaxed)){result=old_head->data;deleteold_head;// 实际工程中需要用 Hazard Pointer 或 RCUreturntrue;}}returnfalse;}};

五、注意事项

说明
不是万能锁原子操作适合简单计数/标志,复杂逻辑还是要用mutex
ABA 问题CAS 中值从 A→B→A,CAS 认为没变。解决:带标签的指针(Tagged Pointer)
伪共享 (False Sharing)两个原子变量在同一缓存行,不同 CPU 核频繁修改会互相拖累。解决:alignas(64)
只能用于平凡可复制类型std::atomic<std::string>不行,std::atomic<int>可以
compare_exchange_weak vs strongweak可能伪失败(被中断),适合循环;strong保证真失败才返回 false,适合单次检查

性能对比

std::atomic<int>a{0};// 最慢,但最直观a.store(1);// 等价于 memory_order_seq_cst// 快 2~10 倍(x86 上差距小,ARM 上差距大)a.store(1,std::memory_order_relaxed);

经验法则

  • 如果不需要跨线程的happens-before关系,用relaxed
  • 如果需要发布-订阅模式,用release/acquire配对
  • 如果搞不清,用默认的seq_cst,correctness first

六、单例模式 + 原子操作实战

6.1 基于 atomic 的双检查锁(DCLP)

C++11 之前 DCLP 有指令重排序问题,C++11 引入std::atomic后可以安全实现。

#include<atomic>#include<mutex>#include<iostream>classSingleton{public:staticSingleton*getInstance(){Singleton*tmp=instance.load(std::memory_order_acquire);if(tmp==nullptr){// 第一次检查(无锁)std::lock_guard<std::mutex>lock(mutex);// 加锁tmp=instance.load(std::memory_order_relaxed);if(tmp==nullptr){// 第二次检查(有锁)tmp=newSingleton();// release: 保证 new Singleton() 的构造完成// 对其它线程的 acquire 可见instance.store(tmp,std::memory_order_release);}}returntmp;}voiddoSomething(){std::cout<<"Singleton::doSomething()\n";}private:Singleton()=default;~Singleton()=default;Singleton(constSingleton&)=delete;Singleton&operator=(constSingleton&)=delete;staticstd::atomic<Singleton*>instance;staticstd::mutex mutex;};std::atomic<Singleton*>Singleton::instance{nullptr};std::mutex Singleton::mutex;// 使用intmain(){Singleton*s=Singleton::getInstance();s->doSomething();}

关键点

  • instance.load(std::memory_order_acquire):确保看到其他线程release的完整构造对象
  • instance.store(tmp, std::memory_order_release):确保构造完成后再发布指针
  • 双重检查:第一次无锁快速路径,第二次有锁保证唯一性

6.2 基于 atomic_flag 的无锁单例(更轻量)

如果单例构造很快,可以用atomic_flag实现一个轻量级自旋锁版本的单例。

#include<atomic>#include<thread>#include<iostream>#include<vector>classSpinLockSingleton{public:staticSpinLockSingleton*getInstance(){// 快速路径:已初始化直接返回SpinLockSingleton*tmp=instance.load(std::memory_order_acquire);if(tmp!=nullptr){returntmp;}// 慢速路径:自旋等待初始化完成while(flag.test_and_set(std::memory_order_acquire)){// 自旋中,检查是否其他线程已经初始化完成tmp=instance.load(std::memory_order_relaxed);if(tmp!=nullptr){flag.clear(std::memory_order_release);// 释放锁(虽然没拿到)returntmp;}}// 拿到锁,执行初始化tmp=instance.load(std::memory_order_relaxed);if(tmp==nullptr){tmp=newSpinLockSingleton();instance.store(tmp,std::memory_order_release);std::cout<<"Singleton initialized by thread\n";}flag.clear(std::memory_order_release);returntmp;}intgetValue()const{returnvalue;}private:SpinLockSingleton():value(42){}~SpinLockSingleton()=default;staticstd::atomic<SpinLockSingleton*>instance;staticstd::atomic_flag flag;intvalue;};std::atomic<SpinLockSingleton*>SpinLockSingleton::instance{nullptr};std::atomic_flag SpinLockSingleton::flag=ATOMIC_FLAG_INIT;// 多线程测试intmain(){std::vector<std::thread>threads;for(inti=0;i<10;++i){threads.emplace_back([](){auto*s=SpinLockSingleton::getInstance();std::cout<<"Value: "<<s->getValue()<<"\n";});}for(auto&t:threads)t.join();}

6.3 基于 call_once 的现代写法(推荐生产环境)

虽然这不是纯 atomic 实现,但它是最安全的现代 C++ 单例写法,底层由标准库保证原子性。

#include<mutex>#include<atomic>#include<iostream>classModernSingleton{public:staticModernSingleton&getInstance(){std::call_once(initFlag,&ModernSingleton::init);return*instance;}voiddoSomething(){std::cout<<"ModernSingleton::doSomething(), value="<<value<<"\n";}private:ModernSingleton():value(100){}~ModernSingleton()=default;staticvoidinit(){instance=newModernSingleton();}staticstd::once_flag initFlag;staticModernSingleton*instance;intvalue;};std::once_flag ModernSingleton::initFlag;ModernSingleton*ModernSingleton::instance=nullptr;

6.4 基于 static local variable 的最简写法(C++11 起线程安全)

classSimpleSingleton{public:staticSimpleSingleton&getInstance(){staticSimpleSingleton instance;// C++11 起保证线程安全初始化returninstance;}voiddoSomething(){}private:SimpleSingleton()=default;~SimpleSingleton()=default;SimpleSingleton(constSimpleSingleton&)=delete;SimpleSingleton&operator=(constSimpleSingleton&)=delete;};

C++11 标准规定:函数内的static局部变量初始化是线程安全的,由编译器插入原子操作保证。这是最推荐的写法。


七、单例模式选择指南

实现方式线程安全性能复杂度推荐场景
static local✅ C++11 标准保证最优(无锁)最低首选,绝大多数场景
std::call_once需要延迟初始化且构造可能抛异常
atomic DCLP学习原子操作原理、需要手动控制内存序
atomic_flag 自旋锁构造极快、不想用 mutex
mutex一般简单场景,不追求极致性能

八、一句话总结

std::atomic提供无锁的原子操作,通过内存序控制可见性和重排序。relaxed最快但最弱,release/acquire是性能与正确性的平衡点,seq_cst最安全但最慢。CAS 是实现无锁数据结构的基石,要小心 ABA 和伪共享。单例模式优先用 C++11 的static local变量,学习原子操作原理时可以用atomic DCLP实现。

声明:本文档由 AI(Moonshot AI / Kimi)辅助生成,内容仅供参考,请根据实际情况核实和使用。 仅供学习使用

http://www.jsqmd.com/news/1289689/

相关文章:

  • 上海浦东公司附近汽修保养怎么选?3类门店实测对比+FAQ - qiqi1113
  • 跨境网站线上客服海外隐私合规设置规范
  • 2026年南京GEO服务商五强榜单:谁在真正帮品牌抢占AI搜索答案位? - 天涯视角
  • 从零实现一个可观测的 Python 异步任务重试器:超时、退避、日志与指标
  • 2026年广州糖水加盟怎么选?黄氏同信帮你避坑 - 天涯视角
  • Ninjabrain-Bot界面详解:自定义主题与设置优化指南
  • Antho-RPamide I ;LPPGPLPRP-NH₂
  • Blind Index常见问题解答:开发者必知的10个要点
  • 单片机毕业设计-基于 STM32F103 的加湿设备智能控制系统设计 基于 DHT11 的环境监测与继电器控制装置设计(011601)
  • Inspectus入门教程:3步上手LLM注意力矩阵分析
  • 2026江苏公考机构实力横评:粉笔综合96.8分断层领跑,华图中公紧随其后 - 天涯视角
  • RK3506 软件解码播放视频
  • 2026年真空除氧器行业实力厂家与供应商综合解析 - 优企名品
  • Web安全实战:从信息搜集到权限提升的完整渗透测试路径解析
  • 2026实力之选:长沙市智行地坪工程有限公司——球场地坪施工领域的专业化服务力量 - 优企名品
  • LeCun强推了一个3b小模型,你的cpu都能跑
  • 日记第19天——思考
  • 企业AI落地服务杭州懂自己BOSS增长营设备预测维护AI方案:从数据采集到故障预警的系统架构与实战路径
  • 2026 年现阶段二道有实力的球场围网定制选哪家,你家球场的隐形屏障,居然藏着这么多省钱的小门道?-博才金属网业 - 企业推荐官【认证】
  • 上海黄浦区公寓装修哪家强?这家设计团队超牛的公司了解下
  • 舆情预警失效真相(千万级舆情漏报事件深度复盘)
  • Ubiquity视图引擎全解析:Twig、Micro与自定义模板技巧
  • 全硅皮沙发公司 - 米諾
  • 做海报还在等设计排期?2026年七夕海报用什么AI工具可以做?6款工具横评 - 子柔传媒
  • 在 Seq2Seq 模型中,编码器和解码器各负责什么功能?
  • 使用Frida-trace逆向分析iOS应用Apple服务认证签名机制
  • notepad++使用方法
  • 2026广州糖水加盟首选这三家,口碑与盈利双赢 - 天涯视角
  • 2026多语言数字人视频怎么测:术语表、字幕与版本回归
  • 一文讲清广州房产税土地税申报公司口碑好:广州机构推荐测评及横向对比 - 米諾