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

[C++][多线程]原子操作

[C++]原子操作

C++11引入了std::atomic,为并发编程提供了原子操作。

原子操作是不可分割的操作,要么完全执行,要么完全不执行,不会被其他线程中断。在多线程环境中,原子操作保证了数据的一致性,避免了竞态条件。

与锁的比较

既然老版本的C++提供了锁来解决数据竞争和同步问题,为何还要使用原子操作呢?其实是对性能的考虑。我们都学习过,锁的主要开销在于锁竞争激烈时发生的系统调用和上下文切换;而原子操作具有·无上下文切换、防止编译器重排序的特点。接下来通过代码对比一下:

#include <iostream>
#include <mutex>
#include <thread>
#include <chrono>
#include <atomic>const int num_threads = 5;
const int increments_per_thread = 1000000;
std::mutex mtx;int main() {int counter = 0;auto start = std::chrono::high_resolution_clock::now();std::vector<std::thread> threads;for (int i = 0; i < num_threads; ++i) {threads.emplace_back([&]() {for (int j = 0; j < increments_per_thread; ++j) {counter++; // 不加锁}});}for (auto& t : threads)t.join();auto end = std::chrono::high_resolution_clock::now();auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();std::cout << "No lock final counter value: " << counter << std::endl;std::cout << "Time taken: " << duration << " ms" << std::endl;threads.clear();counter = 0;start = std::chrono::high_resolution_clock::now();for (int i = 0; i < num_threads; ++i) {threads.emplace_back([&]() {for (int j = 0; j < increments_per_thread; ++j) {mtx.lock();counter++;mtx.unlock();}});}for (auto& t : threads)t.join();end = std::chrono::high_resolution_clock::now();duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();std::cout << "Lock final counter value: " << counter << std::endl;std::cout << "Time taken: " << duration << " ms" << std::endl;threads.clear();std::atomic<int> atomic_counter(0);start = std::chrono::high_resolution_clock::now();for (int i = 0; i < num_threads; ++i) {threads.emplace_back([&]() {for (int j = 0; j < increments_per_thread; ++j) {atomic_counter.fetch_add(1, std::memory_order_relaxed); // 宽松序,允许重排}});}for (auto& t : threads)t.join();end = std::chrono::high_resolution_clock::now();duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();std::cout << "Atomic final counter value: " << atomic_counter.load() << std::endl;std::cout << "Time taken: " << duration << " ms" << std::endl;
}

最后输出结果

No lock final counter value: 1367279
Time taken: 24 ms
Lock final counter value: 5000000
Time taken: 408 ms
Atomic final counter value: 5000000
Time taken: 174 ms

可以发现原子操作运行时间更短。

内存序

前面提到原子操作可以防止编译器重排序,这里引入内存序的概念。
内存序用来控制多线程程序中内存操作的可见性顺序。它告诉编译器和CPU:

  1. 哪些重排序是允许的
  2. 哪些重排序是禁止的
  3. 何时需要确保内存操作的可见性
enum memory_order {memory_order_relaxed,    // 宽松序:只保证原子性,允许重排序memory_order_consume,    // 消费序:较少使用,类似acquire但更弱memory_order_acquire,    // 获取序:读操作的同步点,防止后续操作重排到前面memory_order_release,    // 释放序:写操作的同步点,防止前面操作重排到后面memory_order_acq_rel,    // 获取-释放序:同时具有acquire和release语义memory_order_seq_cst     // 顺序一致性:最强的内存序,全局统一顺序
};

而提供六种不同的内存序,一方面是对性能的要求不同,另一方面是对重排序的接受程度不同。

const int num_threads = 5;
const int increments_per_thread = 1000000;
std::atomic<int> atomic_counter(0);int main() {std::vector<std::thread> threads;auto start = std::chrono::high_resolution_clock::now();for (int i = 0; i < num_threads; ++i) {threads.emplace_back([&]() {for (int j = 0; j < increments_per_thread; ++j) {atomic_counter.fetch_add(1, std::memory_order_relaxed); // 宽松序,允许重排}});}for (auto& t : threads)t.join();auto end = std::chrono::high_resolution_clock::now();auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();std::cout << "Relaxed time taken: " << duration << " ms" << std::endl;threads.clear();atomic_counter.store(0); // 重置计数器start = std::chrono::high_resolution_clock::now();for (int i = 0; i < num_threads; ++i) {threads.emplace_back([&]() {for (int j = 0; j < increments_per_thread; ++j) {atomic_counter.fetch_add(1, std::memory_order_seq_cst); // 顺序一致性,禁止重排}});}for (auto& t : threads)t.join();end = std::chrono::high_resolution_clock::now();duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();std::cout << "Seq time taken: " << duration << " ms" << std::endl;
}

这段代码输出

Relaxed time taken: 172 ms
Seq time taken: 180 ms

除了性能差别,对重排的要求又决定了不同内存序有不同的应用场景:对于简单的计数器,用memory_order_relaxed即可,而对于生产者-消费者问题,则需要保证编译器不会重排:

void producer() {data.store(42, std::memory_order_relaxed);    // 1. 写入数据data_ready.store(true, std::memory_order_release);  // 2. 发布信号(不能重排到1之前)
}void consumer() {// 获取信号(后续读取不能重排到这之前)while (!data_ready.load(std::memory_order_acquire)) {std::this_thread::yield();}int value = data.load(std::memory_order_relaxed);std::cout << "consuming: " << value << std::endl;
}

std::atomic的函数

  1. atomic_storeatomic_store_explicit
    设置值,内存序默认为seq_cst,explicit版本可以显示指定内存序
  2. atomic_loadatomic_load_explicit
    读取值
  3. compare_exchange:CAS操作
    有两个相似的函数:
// strong版本:原子地比较并交换,如果比较失败则不交换
bool atomic_compare_exchange_strong_explicit(atomic<T>* obj,T* expected,T desired,memory_order success,memory_order failure
);
// weak版本:原子地比较并交换,可能出现伪失败
bool atomic_compare_exchange_weak_explicit(atomic<T>* obj,T* expected,T desired,memory_order success,memory_order failure
);

至于这两个函数的细节与差别,读者去向AI chat提问一下就明白啦。简要来讲,weak版本允许“伪失败”,在循环中性能更好。
4. atomic_fetch_*:
这一系列函数先读取原子变量的当前值,然后对其进行运算,最后将结果写回,整个过程不可中断,并会返回修改前的原始值,也都有explicit版本。
包括add、sub、and、or、xor

原子操作总结

原子操作能够以更高的性能解决互斥锁解决的数据竞争和同步问题,所以可以应用原子操作以减少代码对互斥锁的依赖,实现无锁数据结构,提高并发效率;关键在于设置合适的内存序,已经在循环中使用weak版本的compare&exchange。

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

相关文章:

  • SEGGER的embOS也推出动态APP用法emApps
  • 别再瞎找了!10个AI论文软件测评:全学科适配,开题报告+毕业论文全搞定
  • 2026年全国拟上市公司股权激励服务排名,靠谱品牌推荐 - 工业设备
  • 【新能源电站运维】运维无效出工减少30%、设备寿命延长3-5年:功率预测如何重构新能源场站成本结构?
  • 专科生也能用!千笔AI,碾压级的AI论文工具
  • Prompt提示词设计工程:从原则到实战的系统性方法论(附模板与调试工具)
  • 揭开Airsim仿真自动UAV巡航无碰撞源码的神秘面纱
  • 三分钟看懂财报的方法 - 智慧园区
  • 吐血推荐! AI论文写作软件 千笔ai写作 VS PaperRed,专科生专属神器!
  • AI产品经理核心能力全景图:从需求洞察到产品落地的全链路实战手册
  • 【高精度气象】光伏运维的“清洗经济学”:精准辐照预报如何让每一块面板都在最佳时刻“吐纳”
  • 从此告别拖延 10个降AIGC平台全场景通用测评与推荐
  • 功率波动平抑:从算法到并网标准验证
  • 【高精度气象】一场暴雨影响多少赛事赞助?赛事保险正在依赖分钟级预报止损
  • 讲讲拟上市公司股票期权激励,靠谱的品牌有哪些可推荐? - mypinpai
  • 2026年全国矫平机品牌制造厂技术强排名,这些厂家值得关注 - myqiye
  • 干货来了:本科生专属降AI率平台,千笔·专业降AI率智能体 VS 锐智 AI
  • 细聊2026年厚板材料整平机,安徽中诺一智能机械有限公司品牌性价比高不高? - 工业品牌热点
  • 2026年分板机制造商费用大揭秘,EXE亿协性价比超高 - myqiye
  • 聊聊2026复合式分板机生产厂 哪家技术强 如何选择 - mypinpai
  • 天猫超市卡回收方法大揭秘 - 团团收购物卡回收
  • 全国范围内矫平机怎么选择,稳定性好和适合批量生产的品牌 - 工业推荐榜
  • 天猫超市卡快速回收攻略,一分钟搞定! - 团团收购物卡回收
  • 2026年全国压花机制造厂排名,推荐质量好、设备先进且经验丰富的品牌 - 工业品牌热点
  • 剖析2026年性价比高的矫平机按需定制厂家,数控液压精密矫平机怎么选 - 工业品网
  • 立体仓储市场观察:服务优质的几家企业解析,立体仓库/自动化立体仓库/全自动仓库/智能仓库/高架库,立体仓储供应商排名 - 品牌推荐师
  • 六大城市高端腕表应急维修指南:36品牌场景化养护+突发故障速解技巧 - 时光修表匠
  • 复合式分板机生产厂哪家售后好,费用大概多少钱? - 工业设备
  • 盘点全国不错的绩效薪酬咨询企业,创锟咨询靠谱吗? - 工业品牌热点
  • 2026年质量好的消防风机厂家推荐:离心式排烟消防风机实力工厂怎么选 - 行业平台推荐