news 2026/4/18 13:15:08

C++多线程之原子操作 std::atomic

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
C++多线程之原子操作 std::atomic

std::atomic 介绍

std::atomic 是 C++11 引入的模板类,用于支持多线程环境下的原子操作。原子操作是不可分割的操作,即在执行过程中不会被其他线程打断,从而避免数据竞争和未定义行为。

原子操作的概念

原子操作是指一个操作要么完全执行,要么完全不执行,不会出现部分执行的状态。在多线程环境中,原子操作能够确保对共享变量的读写是线程安全的,无需额外的锁机制。

适用场景

  • 计数器或标志位的无锁更新
  • 实现简单的同步机制(如自旋锁)
  • 高性能并发场景,避免锁的开销

atomic仅能处理 “单个变量的原子读写 / 交换 / 累加”(如计数器、标志位),一旦逻辑超过 “单个变量操作”,atomic就无能为力。atomic的核心价值就是无锁、高性能的单一变量同步

常用接口

std::atomic 提供以下常用成员函数:

  • load():原子读取变量的值
  • store(val):原子写入变量的值
  • exchange(val):原子交换变量的值并返回旧值
  • compare_exchange_weak(expected, desired):比较并交换(弱版本)
  • compare_exchange_strong(expected, desired):比较并交换(强版本)

经典应用场景

1.std::atomic用来做线程退出的标志位

//设置线程退出标志位 std::atomic <bool> m_flag; //假设有一个工作线程 void work(){ while(!m_flag.load(std::memory_order_acquire)) { std::cout<<"this thread is working..."<<std::endl; std::this_thread::sleep_for(std::chrono::milliseconds(100)); } } int main() { std::thread t(work); std::this_thread::sleep_for(std::chrono::seconds(1)); //更改线程标志位 m_flag.store(true,std::memory_release) //std::memory_release规定写入权限对于线程可见 t.join(); return 0; }
2.使用 std::atomic_flag 实现自旋锁

std::atomic_flag 是最简单的原子布尔类型,适合实现自旋锁

#include <atomic> #include <thread> class SpinLock { public: SpinLock() : flag(ATOMIC_FLAG_INIT) {} void lock() { while (flag.test_and_set(std::memory_order_acquire)) { // 核心条件2: 满足自旋等待,不阻塞 } } void unlock() { flag.clear(std::memory_order_release); } private: std::atomic_flag flag; //核心条件1:原子操作 }; // 使用示例 SpinLock spinLock; int sharedData = 0; void increment() { spinLock.lock(); ++sharedData; spinLock.unlock(); } int main() { std::thread t1(increment); std::thread t2(increment); t1.join(); t2.join(); return 0; }

1.std::atomic_flag是一个原子布尔开关,其只有std::atomic_flag::test_and_set()std::atomic_flag::clear()两个接口。

2.std::atomic::test_and_set(): 有两个返回值:1.false-表示当前锁未被占用

2.true-表示当前锁已被占用

返回的是旧值(即标志位被更改之前的值)。其核心逻辑是原子性的完成(测试标志位状态+设置标志位为置位)两个动作,其动作不可被线程调度打断,要么完成,要么不进行。

可能有的读者会误认为,test_and_set()是先测试锁是否被占用,没有被占用再获取锁,并将其标志位改为置用。 但其实并不是这样,其操作逻辑是先获取标志位状态,并且不管当前锁有没有没占用,都将标志位改为占用,再返回获取到的未被更改前的旧值。

例如,test_and_set()获取到的是false,则先将false改为true,告诉其他线程锁已经被我占用了,再将返回获取到的false。

若其获取到的是true,还是会将标志位置为true,这就可以理解为不管获取到什么状态,都会将标志位强行置为true。返回true,继续忙等。

3.std::atomic_flag::clear(): 其作用就是重置标志位,将true置为false。与test_and_set()搭配使用。

注意事项

  • 自旋锁适用于临界区执行时间短的场景,否则会浪费 CPU 资源。
  • 内存序(如std::memory_order_acquirestd::memory_order_release)用于控制原子操作的同步语义。
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/18 8:46:35

Vim快速移动终极指南:EasyMotion与Sneak插件深度对比

Vim快速移动终极指南&#xff1a;EasyMotion与Sneak插件深度对比 【免费下载链接】vim-galore :mortar_board: All things Vim! 项目地址: https://gitcode.com/gh_mirrors/vi/vim-galore 还在为Vim中繁琐的光标移动而烦恼吗&#xff1f;每次在长文档中寻找特定位置&…

作者头像 李华
网站建设 2026/4/18 10:07:27

21、数字 FIR 滤波器的逐步设计

数字 FIR 滤波器的逐步设计 1. FIR 滤波器类型总结 不同类型的 FIR 滤波器在设计不同类型的滤波器时具有不同的适用性。通过 Matlab 分析可知: - 类型 I,正对称,在设计各类滤波器时最为通用。 - 类型 II,仅用于奇数阶滤波器。 - 类型 III 和 IV,常用于设计微分器。 …

作者头像 李华
网站建设 2026/4/18 7:42:10

9、VMware虚拟机配置与Windows系统使用指南

VMware虚拟机配置与Windows系统使用指南 1. 虚拟机内存设置 虚拟机的最佳内存值取决于客户机系统的效率以及在其下运行的应用程序,就像在真实机器上一样。VMware Workstation会根据客户机系统给出推荐值。一般来说,Windows系统比其他操作系统需要更多内存,旧版操作系统比新…

作者头像 李华
网站建设 2026/4/18 2:34:31

14、深入探索VMware中Linux系统的使用与设备管理

深入探索VMware中Linux系统的使用与设备管理 1. 开启X Window系统之旅 现在,你已准备好踏入X Window系统的世界。启动X会话有多种方式,你既可以使用 startx 或 xinit 命令,也能激活显示管理器,像gdm(GNOME)、kdm(KDE)或者xdm。 若在使用XF86_VMware服务器时遇到…

作者头像 李华
网站建设 2026/4/17 15:28:01

17、虚拟化环境下操作系统的使用与配置指南

虚拟化环境下操作系统的使用与配置指南 1. FreeBSD 设备管理 在 FreeBSD 系统中,设备管理有着独特的方式。 - USB 设备管理 :FreeBSD 通过 usbd 守护进程来管理 USB 设备,它主要负责设备的连接和移除操作。若要在系统启动时激活该守护进程,需在 /etc/rc.conf (或…

作者头像 李华
网站建设 2026/4/18 10:06:58

Wechaty微信机器人开发完整指南:从入门到精通的终极教程

Wechaty微信机器人开发完整指南&#xff1a;从入门到精通的终极教程 【免费下载链接】wechaty 项目地址: https://gitcode.com/gh_mirrors/wec/wechaty 微信机器人开发一直是技术领域的热门话题&#xff0c;但传统的开发方式往往面临接口复杂、消息处理困难等挑战。Wec…

作者头像 李华