【单机锁】实现原理

文章目录

  • 1.互斥锁 sync.Mutex 的实现原理;
    • 1.1获取策略有如下两种:
    • 1.2sync.Mutex的方案
      • 1.2.1具体方案如下:
      • 1.2.2转换的条件:
      • 1.2.3运行的两种模式:
      • 1.2.4两种模式的转换条件
      • 1.2.5唤醒标识:
    • 1.3源码走读
  • 2. sync.RWMutex 的实现原理
    • 2.1核心机制
    • 2.2读锁源码走读
    • 2.3写锁源码走读

1.互斥锁 sync.Mutex 的实现原理;

1.1获取策略有如下两种:

• 阻塞/唤醒:将当前 goroutine 阻塞挂起,直到锁被释放后,以回调的方式将阻塞 goroutine 重新唤醒,进行锁争夺;
• 自旋 + CAS:基于自旋结合 CAS 的方式,重复校验锁的状态并尝试获取锁,始终把主动权握在手中.
上述方案各有优劣,且有其适用的场景:
在这里eeee插入图片描述

1.2sync.Mutex的方案

1.2.1具体方案如下:

• 首先保持乐观,goroutine 采用自旋 + CAS 的策略争夺锁;
• 尝试持续受挫达到一定条件后,判定当前过于激烈,则由自旋转为 阻塞/挂起模式.

1.2.2转换的条件:

• 自旋累计达到 4 次仍未取得战果;
• CPU 单核或仅有单个 P 调度器;(此时自旋,其他 goroutine 根本没机会释放锁,自旋纯属空转);
• 当前 P 的执行队列中仍有待执行的 G. (避免因自旋影响到 GMP 调度效率).

1.2.3运行的两种模式:

正常模式/非饥饿模式:这是 sync.Mutex 默认采用的模式. 当有 goroutine 从阻塞队列被唤醒时,会和此时先进入抢锁流程的 goroutine 进行锁资源的争夺,假如抢锁失败,会重新回到阻塞队列头部.(值得一提的是,此时被唤醒的老 goroutine 相比新 goroutine 是处于劣势地位,因为新 goroutine 已经在占用 CPU 时间片,且新 goroutine 可能存在多个,从而形成多对一的人数优势,因此形势对老 goroutine 不利.)
饥饿模式:这是 sync.Mutex 为拯救陷入饥荒的老 goroutine 而启用的特殊机制,饥饿模式下,锁的所有权按照阻塞队列的顺序进行依次传递. 新 goroutine 进行流程时不得抢锁,而是进入队列尾部排队.

1.2.4两种模式的转换条件

• 默认为正常模式;
• 正常模式 -> 饥饿模式:当阻塞队列存在 goroutine 等锁超过 1ms 而不得,则进入饥饿模式;
• 饥饿模式 -> 正常模式:当阻塞队列已清空,或取得锁的 goroutine 等锁时间已低于 1ms 时,则回到正常模式.
小结:

正常模式灵活机动,性能较好;饥饿模式严格死板,但能捍卫公平的底线. 因此,两种模式的切换体现了 sync.Mutex
为适应环境变化,在公平与性能之间做出的调整与权衡. 回头观望,这一项因地制宜、随机应变的能力正是许多优秀工具所共有的特质.

1.2.5唤醒标识:

sync.Mutex 通过一个 mutexWoken 标识位,标志出当前是否已有 goroutine 在自旋抢锁或存在 goroutine 从阻塞队列中被唤醒;倘若 mutexWoken 为 true,且此时有解锁动作发生时,就没必要再额外唤醒阻塞的 goroutine 从而引起竞争内耗。缓解竞争压力和性能损耗。

1.3源码走读

数据结构:
方法:
lock

快速路径通过 CompareAndSwapInt32
尝试以原子的方式获取未被锁定的锁。如果这一步失败,表示锁已被持有或处于某种特殊状态(例如,饥饿模式),则会进入慢速路径 lockSlow

• 首先进行一轮 CAS 操作,假如当前未上锁且锁内不存在阻塞协程,则直接 CAS 抢锁成功返回;
• 第一轮初探失败,则进入 lockSlow 流程,下面细谈.
lockSlow
a.自旋获取锁:

  • 如果锁已被持有且未进入饥饿模式,并且当前 goroutine 可以自旋(runtime_canSpin),则进入自旋。
  • 尝试设置 mutexWoken 标志,告知 Unlock 不唤醒其他阻塞的 goroutine。
  • 执行自旋操作(runtime_doSpin),增加自旋计数并更新 old 状态。

b.更新状态:

  • 构建新的状态 new。
  • 如果不在饥饿模式,尝试设置 mutexLocked 标志,表示尝试获取锁。
  • 如果锁已被持有或处于饥饿模式,增加等待者计数。
  • 如果当前进入饥饿模式且锁已被持有,设置 mutexStarving 标志。
  • 如果 goroutine 被唤醒,清除 mutexWoken 标志。

c.状态更新成功

  • 如果状态更新成功且锁未被持有或不在饥饿模式,跳出循环,表示成功获取锁。
  • 否则,进入阻塞等待(runtime_SemacquireMutex),并更新 starving 状态。
  • 如果在饥饿模式下被唤醒,修复不一致的状态,退出饥饿模式。

d.状态更新失败:

  • 如果状态更新失败,重新获取锁状态并继续循环。
    竞态条件检测:
  • 如果启用了竞态条件检测,调用 race.Acquire 检查竞态条件。

Unlock

  • 通过原子操作解锁;
  • 倘若解锁时发现,目前参与竞争的仅有自身一个 goroutine,则直接返回即可;
  • 倘若发现锁中还有阻塞协程,则走入 unlockSlow 分支.
    unlockSlow
  • 状态一致性检查:确保解锁操作正确,锁未被意外解锁。
  • 非饥饿模式处理:
    • 如果没有等待的 goroutine 或锁状态复杂,则不需要进一步操作。
    • 如果有等待的 goroutine,尝试更新状态并唤醒一个。
  • 饥饿模式处理:
    • 直接将锁的所有权传递给下一个等待的 goroutine,并允许其立即运行。

2. sync.RWMutex 的实现原理

2.1核心机制

  • 从逻辑上,可以把 RWMutex 理解为一把读锁加一把写锁;
  • 写锁具有严格的排他性,当其被占用,其他试图取写锁或者读锁的 goroutine 均阻塞;
  • 读锁具有有限的共享性,当其被占用,试图取写锁的 goroutine 会阻塞,试图取读锁的 goroutine 可与当前 goroutine 共享读锁;
  • 综上可见,RWMutex 适用于读多写少的场景,最理想化的情况,当所有操作均使用读锁,则可实现去无化;最悲观的情况,倘若所有操作均使用写锁,则 RWMutex 退化为普通的 Mutex.

2.2读锁源码走读

RLock

  • 基于原子操作,将 RWMutex 的 readCount 变量加一,表示占用或等待读锁的 goroutine 数加一;
  • 倘若 RWMutex.readCount 的新值仍小于 0,说明有 goroutine 未释放写锁,因此将当前 goroutine 添加到读锁的阻塞队列中并阻塞挂起.

RUnlock

  • 基于原子操作,将 RWMutex 的 readCount 变量加一,表示占用或等待读锁的 goroutine 数减一;
  • 倘若 RWMutex.readCount 的新值小于 0,说明有 goroutine 在等待获取写锁,则走入 RWMutex.rUnlockSlow 的流程中.

rUnlockSlow

  • 对 RWMutex.readerCount 进行校验,倘若发现当前协程此前未抢占过读锁,或者介入读锁流程的 goroutine 数量达到上限,则抛出 fatal;
  • 基于原子操作,对 RWMutex.readerWait 进行减一操作,倘若其新值为 0,说明当前 goroutine 是最后一个介入读锁流程的协程,因此需要唤醒一个等待写锁的阻塞队列的 goroutine.

2.3写锁源码走读

Lock

  • 对 RWMutex 内置的互斥锁进行加锁操作;
  • 基于原子操作,对 RWMutex.readerCount 进行减少 -rwmutexMaxReaders 的操作;
  • 倘若此时存在未释放读锁的 gouroutine,则基于原子操作在 RWMutex.readerWait 的基础上加上介入读锁流程的 goroutine 数量,并将当前 goroutine 添加到写锁的阻塞队列中挂起.

Unlock

  • 基于原子操作,将 RWMutex.readerCount 的值加上 rwmutexMaxReaders;
  • 倘若发现 RWMutex.readerCount 的新值大于 rwmutexMaxReaders,则说明要么当前 RWMutex 未上过写锁,要么介入读锁流程的 goroutine 数量已经超限,因此直接抛出 fatal;
  • 因此唤醒读锁阻塞队列中的所有 goroutine;(可见,竞争读锁的 goroutine 更具备优势)
  • 解开 RWMutex 内置的互斥锁.

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

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

相关文章

猫头虎分享:PyTorch异常ModuleNotFoundError: No module named ‘torch’解决方案

🐯 猫头虎分享:PyTorch异常ModuleNotFoundError: No module named ‘torch’解决方案 💻 摘要 在本篇博客中,我们将深入探讨如何解决PyTorch中常见的“ModuleNotFoundError: No module named ‘torch’”错误。通过详细的步骤指…

差分法求解 Burgers 方程(附完整MATLAB 及 Python代码)

Burgers 方程的数值解及误差分析 引言 Burgers 方程是一个非线性偏微分方程,在流体力学、非线性声学和交通流理论中有广泛应用。本文将通过数值方法求解带粘性的 Burgers 方程,并分析其误差。 方程模型 Burgers 方程的形式为: u t u u …

Jmeter下载、安装、永久汉化(Windows环境)

1、JDK下载 JDK8下载地址https://www.oracle.com/java/technologies/downloads/#java8-windows JDK8的Windows的64位: 2、Jmeter下载 jmeter下载地址https://jmeter.apache.org/download_jmeter.cgi 3、配置环境变量 安装好后,把jdk和jmeter都配置到…

Docker从入门到实践教程(电子版)

前言 Docker 是个伟大的项目,它彻底释放了虚拟化的威力,极大降低了云计算资源供应的成本,同时让应用的 分发、测试、部署和分发都变得前所未有的高效和轻松! 本电子书既适用于具备基础 Linux 知识的 Docker 初学者,也…

隧道可视化:实时监控保障行车安全

通过图扑可视化实现隧道的实时监控、数据分析及智能报警系统,提供全面的隧道管理和决策支持,提升行车安全,优化维护策略,确保交通顺畅。

【b站-湖科大教书匠】6 应用层 - 计算机网络微课堂

课程地址:【计算机网络微课堂(有字幕无背景音乐版)】 https://www.bilibili.com/video/BV1c4411d7jb/?share_sourcecopy_web&vd_sourceb1cb921b73fe3808550eaf2224d1c155 目录 6 应用层 6.1 应用层概述 6.2 客户-服务器方式和对等方…

PsExec横向:IPCPTHPTT

一.IPC下的PsExec 二.PTH下的psexec(CS操作) 三.PTT下的psexec PsExec工具: psexec 是 windows 下非常好的一款远程命令行工具。psexec的使用不需要对方主机开方3389端口,只需要对方开启admin$共享和ipc$ (该共享默认开启&#…

Spring boot 后端向前端发送日期时间发现少了8小时

问题 数据库 后端的控制台输出 前端控制台输出 可以发现少了8小时 问题 springboot 向前端响应数据是默认 Json 格式,所以会有类型转换,springboot 就通过 Jackson 来对 data 类型数据进行转换,但是Jackson 类型的时区是 GMT,与…

Google AI非坦途

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗?订阅我们的简报,深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同,从行业内部的深度分析和实用指南中受益。不要错过这个机会,成为AI领…

Pytorch框架之神经网络

一、全连接神经网络的整体结构 二、全连接神经网络的单元结构 找出一组w,b使得结果最优 三、常见激活函数 四、前向传播 学习率是指训练模型时每次迭代更新模型参数的步长。 五、梯度下降法 六、反向传播计算 七、总结 1、准备数据 2、搭建模型 3、开始训练(设置学习率、…

【TS】TypeScript中的接口(Interface):对象类型的强大工具

🌈个人主页: 鑫宝Code 🔥热门专栏: 闲话杂谈| 炫酷HTML | JavaScript基础 ​💫个人格言: "如无必要,勿增实体" 文章目录 TypeScript中的接口(Interface):对象类型的强大工具引言1. 接口的基本概念1.1 什…

【基于PSINS】UKF/SSUKF对比的MATLAB程序

UKF与SSUKF UKF是:无迹卡尔滤波 SSUKF是:简化超球面无迹卡尔曼滤波 UKF 相较于传统的KF算法,UKF能够更好地处理非线性系统,并且具有更高的估计精度。它适用于多种应用场景,如机器人定位导航、目标跟踪、信号处理等。…

【人工智能】深度剖析:Midjourney与Stable Diffusion的全面对比

文章目录 🍊1 如何选择合适的AI绘画工具1.1 个人需求选择1.2 比较工具特点1.3 社区和资源 🍊2 Midjourney VS Stable Diffusion:深度对比与剖析 2.1 使用费用对比 2.2 使用便捷性与系统兼容性对比 2.3 开源与闭源对比 2.4 图片质量对比 2.5 上…

19145 最长无重复子数组

这个问题可以使用滑动窗口的方法来解决。我们可以使用两个指针,一个指向子数组的开始,一个指向子数组的结束。然后我们使用一个哈希表来记录每个元素最后出现的位置。当我们遇到一个已经在子数组中出现过的元素时,我们就将开始指针移动到这个…

Mac文件拷贝到移动硬盘怎么做Mac拷贝之后Win电脑里看不到

在日常使用mac电脑的过程中,我们经常需要将一些重要的文件备份到外部硬盘上,以防止数据丢失或电脑故障。传输文件到硬盘可以通过多种方法实现,比如拖拽或者复制至移动硬盘,但有时也会遇到移动硬盘无法粘贴,或拷贝后无法…

SSRF (服务端请求伪造)

🎼个人主页:金灰 😎作者简介:一名简单的大一学生;易编橙终身成长社群的嘉宾.✨ 专注网络空间安全服务,期待与您的交流分享~ 感谢您的点赞、关注、评论、收藏、是对我最大的认可和支持!❤️ 🍊易编橙终身成长社群&#…

图像生成中图像质量评估指标—PSNR的详细介绍

文章目录 1. 背景介绍2. 实际应用3. 总结和讨论 1. 背景介绍 峰值信噪比(Peak Signal-to-Noise Ratio,简称PSNR)是一种广泛应用于图像和视频处理领域的客观图像质量评价指标。它主要用于衡量图像的噪声水平和图像质量,可以用来评…

Python酷库之旅-第三方库Pandas(051)

目录 一、用法精讲 186、pandas.Series.is_monotonic_increasing属性 186-1、语法 186-2、参数 186-3、功能 186-4、返回值 186-5、说明 186-6、用法 186-6-1、数据准备 186-6-2、代码示例 186-6-3、结果输出 187、pandas.Series.is_monotonic_decreasing属性 187…

嵌入式人工智能(34-基于树莓派4B的红外传感器、紫外传感器、激光传感器)

这三种光传感器都是不可见光传感器,光是由电场和磁场交替传播而形成的波动现象。光是一种电磁辐射,属于电磁波的一种。下图是电磁波的频谱范围,生活中多数光是看不到的,但是确真实存在,本文介绍几种光传感器&#xff0…

C++从入门到起飞之——友元内部类匿名对象对象拷贝时的编译器优化 全方位剖析!

🌈个人主页:秋风起,再归来~🔥系列专栏:C从入门到起飞 🔖克心守己,律己则安 目录 1、友元 2、内部类 3、 匿名对象 4、对象拷⻉时的编译器优化 5、完结散花 1、友元 • 友元提供…