用最生活化的比喻来解释 C++ 中原子锁、互斥锁和自旋锁的区别和用法,让小白也能秒懂!😄

想象你 ​​(线程)​​ 要去公共更衣室 ​​(共享资源,如变量、数据结构)​​ 换衣服。这个更衣室一次只能进一个人,否则就会走光 ​​(数据竞争、数据不一致)​​。怎么安全高效地使用更衣室?三种锁对应三种策略:


🛡 ​​1. 原子锁 (std::atomic) - 智能门禁卡​

  • ​场景:​​ 你 ​​只想放一个钱包​​ (单个变量) 到更衣室的某个小格子里,放完/取出就走。
  • ​原理:​
    • 像带芯片的门禁卡,刷一下门瞬间开/关,整个过程 ​​不可分割​​。
    • CPU 保证读写这个 ​​单一变量​​ 的操作是 ​​原子的​​(Atomic),即不会被别的线程打断。
  • ​C++ 代码:​
    #include <atomic>
    #include <iostream>
    #include <thread>std::atomic<int> sharedWallet(100); // 原子保护的钱包余额void addMoney(int amount) {sharedWallet += amount; // ⚡️原子操作:加载->计算->存储,一步完成不被中断
    }int main() {std::thread t1(addMoney, 50); // 线程1存50std::thread t2(addMoney, 30); // 线程2存30t1.join();t2.join();std::cout << "Final balance: " << sharedWallet << std::endl; // 保证是180 ✅return 0;
    }
  • ​优点:​
    • ⚡️ ​​极快​​:接近普通变量操作速度
    • 🛡️ ​​安全​​:CPU 硬件保证原子性
  • ​缺点:​
    • 🚫 ​​只能保护简单变量​​(整数、指针等),无法保护复杂操作或代码块。
  • ​何时用:​​ 保护单个变量(如计数器、状态标志)。

🔒 ​​2. 互斥锁 (std::mutex) - 传统门锁 + 排队区​

  • ​场景:​​ 你需要 ​​占用整个更衣室换全套衣服​​ (执行一段代码/操作多个变量)。
  • ​原理:​
    1. 🔒 ​​拿锁:​​ 看到门关着(锁被占用)就去 ​​排队睡觉​​(阻塞)。
    2. 🛋️ ​​排队等待:​​ 线程让出CPU,OS把它放到等待队列睡觉。
    3. 🔑 ​​解锁唤醒:​​ 里面的人出来后喊“下一个!”,OS 叫醒排队的线程。
  • ​C++ 代码:​
    #include <iostream>
    #include <thread>
    #include <mutex>
    #include <vector>std::mutex dressingRoomMutex; // 更衣室门锁
    int sharedLocker = 0;        // 更衣室里的储物柜(非原子)void useDressingRoom(int id) {// 🔒 尝试拿锁(如果锁被占,线程会在这里睡觉等待)dressingRoomMutex.lock(); // --- 临界区开始(一次只进一人)---std::cout << "Thread " << id << " is changing...\n";sharedLocker = id;       // 安全操作共享储物柜// 模拟复杂换装过程std::this_thread::sleep_for(std::chrono::milliseconds(100)); // --- 临界区结束 ---dressingRoomMutex.unlock(); // 🔓 开门!喊“下一个!”
    }int main() {std::vector<std::thread> threads;for (int i = 0; i < 5; ++i) {threads.push_back(std::thread(useDressingRoom, i));}for (auto& t : threads) {t.join();}return 0;
    }
  • ​优点:​
    • 🛡️ ​​绝对安全​​:能保护任意复杂代码块和数据。
    • 💤 ​​节省CPU​​:等待时睡觉,不占CPU资源。
  • ​缺点:​
    • 🐢 ​​慢:​​ 线程切换开销大(睡觉->唤醒要几百纳秒到微秒)。
  • ​何时用:​​ 保护复杂操作/代码块(如修改链表、操作文件)。

🔁 ​​3. 自旋锁 (​自旋实现​ / ​std::atomic_flag​) - 旋转不停的倔驴​

  • ​场景:​​ 你 ​​只想进去放个手机就出来​​(操作耗时极短),但更衣室经常爆满。
  • ​原理:​
    • 看到门关着(锁被占)时,​​不!睡!觉!​
    • ​原地疯狂旋转(循环)​​,不断问:“好了没?好了没?...” 🙋♀️🙋♀️🙋♀️
    • 直到里面的人出来,立刻冲进去!
  • ​C++ 代码(用 atomic_flag 实现):​
    #include <atomic>
    #include <thread>// 自旋锁类(实际项目可用std::mutex,这是原理演示)
    class SpinLock {std::atomic_flag flag = ATOMIC_FLAG_INIT; // 原子标志
    public:void lock() {while (flag.test_and_set(std::memory_order_acquire)) { // 疯狂问:锁了吗?// 等待期可加入短暂暂停(CPU优化指令)// __mm_pause(); // (Intel指令) 降低CPU占用}}void unlock() {flag.clear(std::memory_order_release); // 解锁!}
    };SpinLock spinLock; // 倔驴锁
    int quickSharedData = 0;void quickUpdate(int id) {spinLock.lock(); // 🔁 开始倔驴模式:等锁时疯狂循环quickSharedData = id;   // 快速操作数据// ... 其他非常快的操作(< 几十纳秒)spinLock.unlock();      // 结束倔驴模式
    }int main() {std::thread t1(quickUpdate, 1);std::thread t2(quickUpdate, 2);t1.join();t2.join();return 0;
    }
  • ​优点:​
    • ⚡️ ​​响应极快​​:锁释放后能立刻获知(省去OS唤醒时间)。
    • 🧠 ​​简单高效​​:无需OS介入,无线程切换开销。
  • ​缺点:​
    • 🔥 ​​烧CPU:​​ 等待时占满CPU核心(100%忙等)。
    • 🚫 ​​不适长期等待:​​ 如果锁被长期占用,自旋等于自杀式占用CPU。
  • ​何时用:​​ 内核开发、耗时极短的临界区(如更新标志位)、实时系统。

🔍 终极对比表(哪种锁更好?)

特性原子锁 (std::atomic)互斥锁 (std::mutex)自旋锁 (Spin Lock)
​工作机制​CPU 硬件原子指令OS 调度阻塞等待CPU 循环忙等待
​速度​⚡️ 极快 (≈普通变量)⏱️ 较慢 (微秒级)⚡ 很快 (纳秒级响应)
​CPU占用​正常等待时=0等待时=100% ❗️
​保护范围​单个简单变量任意代码块/复杂数据任意代码块/复杂数据
​适用等待时间​无等待 (直接操作)长等待 (毫秒以上)​极短等待​​ (纳秒级)
​使用场景​计数器、状态标志文件IO、复杂数据结构操作高频短操作、内核中断处理

✅ ​​黄金选择法则:​

  1. ​只保护单个变量? → 首选 std::atomic
  2. ​保护复杂操作/代码块? → 首选 std::mutex
  3. ​超高频+超短操作 + 追求极限性能? → 慎用自旋锁​​(需精准评估耗时)

⚠️ ​​重要忠告:​
​别手写自旋锁!​​ 除非你是OS开发者。现代 std::mutex 内部有混合策略(先自旋后阻塞),且C++17提供了更快更智能的 std::shared_mutex。优先使用标准库锁!

用锁就像选更衣室策略:​​简单存包用智能卡(原子锁),全套换装用带排队的门锁(互斥锁),放个手机才当倔驴(自旋锁)!​​ 👏

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/94574.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/94574.shtml
英文地址,请注明出处:http://en.pswp.cn/diannao/94574.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

RabbitMQ面试精讲 Day 12:镜像队列与Quorum队列对比

【RabbitMQ面试精讲 Day 12】镜像队列与Quorum队列对比 开篇&#xff1a;面试价值与核心要点 在RabbitMQ集群环境中&#xff0c;如何保证消息的高可用性是最常被问及的面试问题之一。今天我们将深入探讨RabbitMQ提供的两种高可用队列实现方案&#xff1a;经典镜像队列(Mirror…

Maven 常用命令详解

前言 Apache Maven 是 Java 项目管理和构建自动化工具&#xff0c;它通过一个项目对象模型&#xff08;POM, Project Object Model&#xff09;来管理项目的构建、报告和文档。Maven 的核心优势在于其强大的依赖管理、标准化的项目结构以及丰富的插件生态系统。掌握 Maven 的常…

Android Studio切换到经典UI,老UI

为什么要写这边文章&#xff0c;我自己的android studio年儿半载安装一次新的&#xff0c;所以记录一下。1、新android studio安装后都是新工作室风格的UI。2、菜单File ->Settings ,在Setting界面将Enable New UI 的勾选去掉&#xff0c;点击OK&#xff0c;重启Android Stu…

走进“Mesh无线自组网”:开启智能家居和智慧工厂

随着科技的迅猛发展&#xff0c;互联网已经渗透到我们生活的方方面面&#xff0c;而其中最引人瞩目的便是“智能家居”和“智慧工厂”。这些新兴领域的飞速发展&#xff0c;离不开底层网络技术的支持。众多的网络通信技术中&#xff0c;Mesh无线自组网以其独特的优势&#xff0…

力扣热题100——双指针

双指针两数之和&#xff08;有序数组&#xff0c;相向双指针&#xff09;问题&#xff1a;在有序数组中找到两个数&#xff0c;使它们的和等于目标值。思路&#xff1a;左指针从起点出发&#xff0c;右指针从终点出发&#xff0c;根据和与目标值的大小调整指针。 #include <…

AI Infra与LLM的联系与差异

一、定义与定位LLM&#xff08;大语言模型&#xff09; 定义&#xff1a;基于海量文本训练的深度学习模型&#xff0c;通过Transformer架构实现语言理解与生成&#xff0c;典型代表如GPT-4、通义千问等。定位&#xff1a;AI应用的核心能力层&#xff0c;直接面向用户提供文本生…

数据结构-双链表

学习完单链表&#xff0c;现在继续学习双链表一、双链表结构带头双向循环链表&#xff08;简称&#xff1a;双链表&#xff09;注意&#xff1a;这⾥的“带头”跟前面我们说的“头节点”是两个概念&#xff0c;实际前面的在单链表阶段称呼不严谨&#xff0c;但是为了同学们更好…

福彩双色球第2025090期篮球号码分析

明天是星期四&#xff0c;明天晚上双色球开奖。福彩双色球第2025090期篮球号码分析&#xff0c;上期开出号码05&#xff0c;数字形式是质数奇数2路球&#xff0c;小号0字头数字。本期篮球号码分析&#xff0c;篮球2尾数0212遗漏6期上次遗漏27期&#xff0c;篮球3尾数0313遗漏4期…

Python爬虫实战:研究Photon工具,构建企业信息收集系统

1. 引言 1.1 研究背景 在数字化时代,互联网作为全球最大的信息载体,涵盖商业情报、学术资源、公共信息等多个领域,对企业决策、学术研究和社会治理具有重要参考价值。传统信息获取方式依赖人工检索和简单脚本爬取,存在效率低下、覆盖范围有限、数据处理能力不足等问题。 …

Python Pandas.lreshape函数解析与实战教程

Python Pandas.lreshape 函数解析与实战教程 摘要 本教程旨在提供一份关于Pandas库中 pandas.lreshape 函数的全面使用教程和分析。lreshape 是一个用于数据重塑(Data Reshaping)的工具,具体而言,它擅长将“宽格式”(Wide Format)数据转换为“长格式”(Long Format)数…

vue3 el-dialog自定义实现拖拽、限制视口范围增加了拖拽位置持久化的功能

采用element-plus的拖拽功能代码,在此基础上增加了记忆拖拽上次拖拽位置的功能,开袋即食; 前提:每次关闭弹窗都要销毁; 解决了默认设置transform的偏移量后首次拖拽弹窗偏移量错误的问题修改。<template><el-dialogref="popupRefDialog":title="…

学习嵌入式之硬件——ARM体系

一、ARM内核基础知识1.ALU&#xff1a;算术逻辑单元&#xff1b;完成运算的电路2.通用寄存器&#xff1a;R0~R15R13&#xff08;SP&#xff09;&#xff1a;栈指针寄存器&#xff1a;指向栈顶的位置&#xff1b;并在函数调用、中断处理等场景中自动更新。R14&#xff08;LR&…

微信小程序中使用TensorFlowJS从环境搭建到模型训练及推理模型得到预测结果

1、小程序端环境准备app.json"plugins": {"tfjsPlugin": {"version": "0.2.0","provider": "wx6afed118d9e81df9"}}package.json"dependencies": {"tensorflow-models/posenet": "^2.2.…

深入剖析通用目标跟踪:一项综述

摘要 通用目标跟踪仍是计算机视觉领域一项重要且具有挑战性的任务,其难点在于复杂的时空动态变化,尤其在存在遮挡、相似干扰物和外观变化的情况下。过去二十年间,为应对这些挑战,研究者提出了多种跟踪范式,包括基于孪生网络的跟踪器、判别式跟踪器以及近期突出的基于Tran…

Next.js 链接与导航:页面间无缝切换

链接与导航&#xff1a;页面间无缝切换 关键要点 Next.js 提供了 <Link> 组件和程序化导航方法&#xff0c;实现页面间高效、无缝的切换。<Link> 组件利用客户端导航和预加载技术&#xff0c;优化用户体验和性能。程序化导航通过 useRouter 钩子&#xff08;Page…

根据经纬度(从nc格式环境数据文件中)提取环境因子

根据经纬度&#xff08;从nc格式环境数据文件中&#xff09;提取环境因子 文章目录前言一、准备所需文件二、代码分享总结前言 本文主要利用nc格式环境数据文件和物种经纬度分布文件&#xff0c;根据经纬度&#xff08;从nc格式环境数据文件中&#xff09;提取环境因子 一、准…

Uniapp 自定义 Tabbar 实现教程

Uniapp 自定义 Tabbar 实现教程1. 简介2. 实现步骤2.1 创建自定义 Tabbar 组件2.2 配置 pages.json3.1 路由映射3.2 样式设计3.3 图标处理4. 常见问题及解决方案4.1 页面跳转问题4.2 样式适配问题4.3 性能优化5. 扩展功能5.1 添加徽标5.2 添加动画效果6. 总结1. 简介 在 Uniap…

JuiceFS存储

因语雀与csdn markdown 格式有区别&#xff0c;请查看原文&#xff1a; https://www.yuque.com/dycloud/pss8ys 一、JuiceFS 介绍 1.1 JuiceFS 是什么 JuiceFS 是一款面向云环境设计的高性能 POSIX 文件系统&#xff0c;核心能力是将对象存储转化为全功能文件系统。它采用独…

【HarmonyOS Next之旅】DevEco Studio使用指南(三十八) -> 构建HAR

目录 1 -> 前言 2 -> 使用约束 3 -> 创建模块 4 -> 构建HAR 4.1 -> 以debug模式构建HAR 4.2 -> 以release模式构建HAR 4.3 -> 构建字节码格式的HAR 4.4 -> 对HAR进行签名 1 -> 前言 构建模式&#xff1a;DevEco Studio默认提供debug和rele…

93、【OS】【Nuttx】【构建】cmake menuconfig 目标

【声明】本博客所有内容均为个人业余时间创作&#xff0c;所述技术案例均来自公开开源项目&#xff08;如Github&#xff0c;Apache基金会&#xff09;&#xff0c;不涉及任何企业机密或未公开技术&#xff0c;如有侵权请联系删除 背景 接之前 blog 【OS】【Nuttx】【构建】cm…