c++中如何使用std::atomic_flag_c++最简单的自旋锁实现【详解】

发布时间 - 2026-01-27 00:00:00    点击率:
std::atomic_flag 是最轻量的无锁原子布尔类型,专为实现自旋锁设计,仅支持 test_and_set() 和 clear(),强制 lock-free 且初始化必须用 ATOMIC_FLAG_INIT 或 {}。

std::atomic_flag 是 C++ 中最轻量的原子布尔类型,它不提供 load()store(),只支持 test_and_set()clear() —— 这恰恰让它成为实现**无锁自旋锁**的理想起点。

为什么不用 std::atomic 做自旋锁?

看似更直观,但 std::atomicexchange(true, std::memory_order_acquire) 在某些平台(如 ARM)可能生成较重的指令;而 std::atomic_flag 被标准强制要求是“无锁的”(lock-free),且 test_and_set() 默认使用 std::memory_order_seq_cst,语义更贴近互斥锁的 acquire/release 行为。

  • std::atomic_flag 初始化必须用 ATOMIC_FLAG_INIT(C++17 起可直接用 {} 值初始化)
  • 它不支持拷贝、赋值,天然防误用
  • 没有 operator bool(),必须显式调用 test_and_set()clear()

最简自旋锁:spin_lock 类封装

核心逻辑就两行:循环调用 test_and_set() 直到返回 false(说明之前是未设置状态,抢锁成功);退出临界区时调用 clear()

struct spin_lock {
    std::atomic_flag flag = ATOMIC_FLAG_INIT;

    void lock() {
        while (flag.test_and_set(std::memory_order_acquire)) {
            // 可选:__builtin_ia32_pause() 或 std::this_thread::yield()
        }
    }

    void unlock() {
        flag.clear(std::memory_order_release);
    }
};
  • std::memory_order_acquire 保证 lock 后的读写不被重排到锁获取前
  • std::memory_order_release 保证 unlock 前的读写不被重排到锁释放后
  • 若长期争用,空转消耗 CPU,建议在循环体内加 std::this_thread::yield()

    或 x86 的 _mm_pause()

常见误用与陷阱

看似简单,但几个细节一错就导致死锁或数据竞争:

立即学习“C++免费学习笔记(深入)”;

  • 忘记初始化:写成 std::atomic_flag flag; 是未定义行为 —— 必须用 = ATOMIC_FLAG_INIT{}(C++17)
  • unlock 时用了错误的 memory order:flag.clear(std::memory_order_relaxed) 会破坏同步语义,其他线程可能看不到临界区内的修改
  • 构造函数里没初始化 flag,或多次调用 clear() 而未配对 lock(),会导致锁状态混乱
  • spin_lock 放在栈上并跨线程传递(比如 move 到 lambda)—— 它不可移动、不可拷贝,运行时报错或静默 UB

和 std::mutex 对比:什么时候该用它?

自旋锁只适合「临界区极短 + 争用极少」的场景,比如保护一个计数器更新、或无锁结构中的某个标志位。

  • 优势:无系统调用开销,上下文切换少,在低延迟关键路径中更快
  • 劣势:持有锁时持续占用 CPU,高争用下吞吐反而更差;无法被操作系统调度器挂起,不能用于可能阻塞的操作(如 IO、sleep)
  • 不要试图用它替代 std::mutex 写业务逻辑 —— 几乎总是错的

真正需要它的场合,往往已经处于无锁编程的深水区,此时你大概率已在手写 std::atomic 状态机,而不是靠 std::atomic_flag 打天下。


# 操作系统  #   # c++  # 无锁  # 为什么  # 天下  # 有锁  # 封装  # 构造函数  # bool  # int  # 循环  # Lambda  # 布尔类型  # operator  # 线程  # 布尔  # 死锁  # 不被  # 它不  # 用它  # 最轻  # 几个  # 放在  # 什么时候  # 已在 


相关栏目: 【 网站优化151355 】 【 网络推广146373 】 【 网络技术251813 】 【 AI营销90571


相关推荐: Laravel Seeder填充数据教程_Laravel模型工厂Factory使用  香港服务器网站推广:SEO优化与外贸独立站搭建策略  Laravel如何与Vue.js集成_Laravel + Vue前后端分离项目搭建指南  如何在阿里云虚拟机上搭建网站?步骤解析与避坑指南  Laravel中Service Container是做什么的_Laravel服务容器与依赖注入核心概念解析  php后缀怎么变mp4格式错误_修改扩展名提示格式不对怎么办【技巧】  JavaScript实现Fly Bird小游戏  如何在云服务器上快速搭建个人网站?  如何在云虚拟主机上快速搭建个人网站?  如何安全更换建站之星模板并保留数据?  Python正则表达式进阶教程_复杂匹配与分组替换解析  公司门户网站制作公司有哪些,怎样使用wordpress制作一个企业网站?  Linux安全能力提升路径_长期防护思维说明【指导】  如何快速查询网址的建站时间与历史轨迹?  如何在HTML表单中获取用户输入并结合JavaScript动态控制复利计算循环  EditPlus中的正则表达式实战(5)  实现点击下箭头变上箭头来回切换的两种方法【推荐】  Python结构化数据采集_字段抽取解析【教程】  郑州企业网站制作公司,郑州招聘网站有哪些?  Laravel如何实现本地化和多语言支持?(i18n教程)  网站建设保证美观性,需要考虑的几点问题!  Laravel如何使用Seeder填充数据_Laravel模型工厂Factory批量生成测试数据【方法】  湖南网站制作公司,湖南上善若水科技有限公司做什么的?  Laravel控制器是什么_Laravel MVC架构中Controller的作用与实践  php静态变量怎么调试_php静态变量作用域调试技巧【解答】  html5的keygen标签为什么废弃_替代方案说明【解答】  Laravel怎么写单元测试_PHPUnit在Laravel项目中的基础测试入门  如何确保FTP站点访问权限与数据传输安全?  作用域操作符会触发自动加载吗_php类自动加载机制与::调用【教程】  Laravel怎么实现微信登录_Laravel Socialite第三方登录集成  教你用AI将一段旋律扩展成一首完整的曲子  Laravel如何实现密码重置功能_Laravel密码找回与重置流程  Win11任务栏卡死怎么办 Windows11任务栏无反应解决方法【教程】  Laravel Fortify是什么,和Jetstream有什么关系  Win11怎么关闭专注助手 Win11关闭免打扰模式设置【操作】  如何用搬瓦工VPS快速搭建个人网站?  高防服务器:AI智能防御DDoS攻击与数据安全保障  如何在浏览器中启用Flash_2025年继续使用Flash Player的方法【过时】  黑客入侵网站服务器的常见手法有哪些?  Laravel怎么实现验证码(Captcha)功能  Win11怎么更改系统语言为中文_Windows11安装语言包并设为显示语言  QQ浏览器网页版登录入口 个人中心在线进入  阿里云网站搭建费用解析:服务器价格与建站成本优化指南  php 三元运算符实例详细介绍  Laravel事件和监听器如何实现_Laravel Events & Listeners解耦应用的实战教程  javascript中数组(Array)对象和字符串(String)对象的常用方法总结  如何在新浪SAE免费搭建个人博客?  北京网站制作的公司有哪些,北京白云观官方网站?  Android GridView 滑动条设置一直显示状态(推荐)  浅谈redis在项目中的应用