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

c++ 中 原子锁、互斥锁、自旋锁的区别和详细用法

用最生活化的比喻来解释 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.dtcms.com/a/318562.html

相关文章:

  • 大模型 + 垂直场景:搜索 / 推荐 / 营销 / 客服领域开发
  • 【Redis】Linux部署redis 7集群部署三主三从、ACL新建用户及密码(图文教程)
  • ​ubuntu22.04系统入门 (四)linux入门命令 权限管理、ACL权限、管道与重定向
  • 集合数据类型Map和Set
  • pcl手动直通滤波
  • LeetCode每日一题,8-6
  • 基于Simulink/MWORKS的文字与开关量混合传输系统设计
  • 流式输出 vs 非流式输出
  • SpringBoot设置跨域的几种方式
  • 互斥锁与条件变量
  • 每日五个pyecharts可视化图表-bars(5)
  • Java语言基础深度面试题
  • List、ArrayList 与顺序表
  • 智能学号抽取系统 V5.7.4 更新报告:修复关键同步漏洞,体验更臻完美
  • Spring Boot 项目代码笔记
  • 三、Istio流量治理(二)
  • 文件权限合规扫描针对香港服务器安全基线的实施流程
  • 《零基础入门AI:深度学习入门(从PyTorch安装到自动微分)》
  • Anthropic于本周一推出了其旗舰模型的升级版Claude Opus 4.1
  • 《第十三篇》深入解析 `kb_api.py`:知识库的创建、删除与查询接口
  • 基于Vue 3 的智能支付二维码弹窗组件设计与实现
  • Effective C++ 条款26: 尽可能延后变量定义式的出现时间
  • 007 前端( JavaScript HTML DOM+Echarts)
  • 【保留小数精度不舍入】2022-10-8
  • MaxKB 使用 MCP 连接 Oracle (免安装 cx_Oracle 和 Oracle Instant Client)
  • 智慧水务管理系统
  • C++、STL面试题总结(二)
  • 三、Envoy的管理接口
  • 数据科学与计算pandas
  • 沉寂半年,Kimi归来!