go并发与锁之sync.Mutex入门

sync.Mutex

原理:一个共享的变量,哪个线程握到了,哪个线程可以执行代码

功能:一个性能不错的悲观锁,使用方式和Java的ReentrantLock很像,就是手动Lock,手动UnLock。

使用例子:

var mu sync.Mutex 
var cnt int
func add() {cnt++}
var wg sync.WaitGroup// 管理协程用的,主要是让协程同意结束后再运行调用协程
func main() {for i := 0; i < 1000; i++ {wg.Add(1)go func() {defer wg.Done()defer mu.Unlock()mu.Lock()add()}()}wg.Wait()fmt.Print(cnt)
}
实现原理:

直接看源码好了,

func (m *Mutex) Lock() {// Fast path: grab unlocked mutex.if atomic.CompareAndSwapInt32(&m.state, 0, mutexLocked) { // 能拿到锁,就拿,拿不到就进入慢速模式if race.Enabled {race.Acquire(unsafe.Pointer(m))}return}// Slow path (outlined so that the fast path can be inlined)m.lockSlow()
}

lockSlow()是一个逻辑很多的方法,具体逻辑是:

  1. 自旋尝试:
    • 若当前是正常模式且锁持有时间较短,当前goroutine会自旋(循环检查锁状态),尝试避免立即阻塞。
    • 自旋条件:多核CPU、当前未处于饥饿模式、等待队列为空或自旋次数未超过阈值。
  2. 更新等待计数:
    • 通过原子操作增加state中的等待goroutine计数(高30位)。
  3. 进入阻塞或饥饿模式:
    • 正常模式:若自旋失败,将当前goroutine加入信号量等待队列(sema),并调用runtime_SemacquireMutex阻塞。
    • 饥饿模式:若当前goroutine等待时间超过阈值(1ms),触发饥饿模式。此时新来的goroutine直接进入队列尾部,不再自旋。
func (m *Mutex) lockSlow() {// 初始化变量操作,省略...for {// 这部分处理自旋尝试获取锁的逻辑if old&(mutexLocked|mutexStarving) == mutexLocked && runtime_canSpin(iter) {// 省略...runtime_doSpin()continue}new := old// 如果不是饥饿模式,尝试获取锁(new |= mutexLocked)if old&mutexStarving == 0 {new |= mutexLocked}// 如果锁已被占用或处于饥饿模式,增加等待者计数(new += 1 << mutexWaiterShift)if old&(mutexLocked|mutexStarving) != 0 {new += 1 << mutexWaiterShift}// 如果当前 goroutine 处于饥饿状态且锁被占用,切换到饥饿模式(new |= mutexStarving)if starving && old&mutexLocked != 0 {new |= mutexStarving}// 如果当前 goroutine 是被唤醒的:确保 mutexWoken 标志已设置(否则抛出异常);清除 mutexWoken 标志(new &^= mutexWoken)if awoke {if new&mutexWoken == 0 {throw("sync: inconsistent mutex state")}new &^= mutexWoken}// 尝试用 CAS 更新锁状态if atomic.CompareAndSwapInt32(&m.state, old, new) {if old&(mutexLocked|mutexStarving) == 0 {break }// 决定排队位置:如果是第一次等待(waitStartTime == 0),记录开始等待时间;否则使用 LIFO 顺序(queueLifo = true)queueLifo := waitStartTime != 0if waitStartTime == 0 {waitStartTime = runtime_nanotime()}// runtime_SemacquireMutex 将 goroutine 放入等待队列并阻塞runtime_SemacquireMutex(&m.sema, queueLifo, 2)// 被唤醒后:检查是否等待超时(超过 1ms),更新饥饿状态;重新读取锁状态starving = starving || runtime_nanotime()-waitStartTime > starvationThresholdNsold = m.state// 如果是饥饿模式:if old&mutexStarving != 0 {// 检查状态是否一致if old&(mutexLocked|mutexWoken) != 0 || old>>mutexWaiterShift == 0 {throw("sync: inconsistent mutex state")}// 计算状态增量: 设置 mutexLocked;减少等待者计数;如果不再饥饿或只有一个等待者,退出饥饿模式delta := int32(mutexLocked - 1<<mutexWaiterShift)if !starving || old>>mutexWaiterShift == 1 {delta -= mutexStarving}// 原子更新状态并退出循环atomic.AddInt32(&m.state, delta)break}awoke = trueiter = 0} else {old = m.state}}
}

在这里插入图片描述
Goroutine A 获取锁(Lock()快速路径成功)。
Goroutine B 尝试获取锁,进入慢速路径:
自旋数次后失败,增加等待计数,进入队列阻塞。
Goroutine A 释放锁(Unlock()):
唤醒Goroutine B,新来的Goroutine C可与B竞争锁。

在这里插入图片描述
Goroutine B 等待超过1ms,触发饥饿模式。
Goroutine C 新到达,直接进入队列尾部,不自旋。
Goroutine A 释放锁:
直接将锁交给队列头部的Goroutine B。
Goroutine B 释放锁后,若队列中无等待者,退出饥饿模式。

作者:ShanekAI
链接:https://juejin.cn/post/7488246529430487077
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

整体逻辑大致来说就是:Go 的 sync.Mutex 在竞争不激烈时,会采用短暂的 自旋锁 机制。自旋锁允许 Goroutine 在一小段时间内忙等待,而不是立即进入阻塞状态。这种策略避免了频繁的上下文切换开销。如果激烈的话,就进入饥饿模式,更改了逻辑,在饥饿模式里,停止自旋,直接将当前协程加入等待队列。当前线程执行完毕了,如果是饥饿模式,会把队列里第一个拿出来唤醒。

名词解释:

自旋:就是忙等,就是最简单的例子:

for state == 1{} // 不停地遍历,就好像在不停地自我旋转一样;直到state被其他线程修改了,才停止

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

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

相关文章

【HarmonyOS5】DevEco Studio 使用指南:代码阅读与编辑功能详解

⭐本期内容&#xff1a;【HarmonyOS5】DevEco Studio 使用指南&#xff1a;代码阅读与编辑功能详解 &#x1f3c6;系列专栏&#xff1a;鸿蒙HarmonyOS&#xff1a;探索未来智能生态新纪元 文章目录 前言代码阅读代码导航功能代码折叠语法高亮跨语言跳转代码查找 快速查阅API接口…

【Python 深度学习】1D~3D iou计算

一维iou 二维 import numpy as npdef iou_1d(set_a, set_b):# 获得集合A和B的边界 x1, x2 set_ay1, y2 set_b# 计算交集的上下界low max(x1,y1)high - min(x2, y2)# 计算交集if high - low < 0:inter 0else:inter high - low# 计算并集union (x2 -x1) (y2 - y1) - in…

SpringBoot Controller接收参数方式, @RequestMapping

一. 通过原始的HttpServletRequest对象获取请求参数 二. 通过Spring提供的RequestParam注解&#xff0c;将请求参数绑定给方法参数 三. 如果请求参数名与形参变量名相同&#xff0c;直接定义方法形参即可接收。(省略RequestParam) 四. JSON格式的请求参数(POST、PUT) 主要在PO…

智能防护实战:从攻击成本看企业安全降本增效

1. 网络攻击的低成本与高回报陷阱 暗网中&#xff0c;一次完整的网络钓鱼攻击仅需30美元/月起步&#xff0c;而勒索软件攻击成本平均1000美元&#xff0c;却能导致企业损失高达445万美元&#xff08;IBM 2023年数据&#xff09;。例如&#xff0c;信用卡信息每条仅售10美元&am…

大语言模型 20 - MCP 在客户端中使用 Cursor Cline 中配置 MCP 服务

MCP 基本介绍 官方地址&#xff1a; https://modelcontextprotocol.io/introduction “MCP 是一种开放协议&#xff0c;旨在标准化应用程序向大型语言模型&#xff08;LLM&#xff09;提供上下文的方式。可以把 MCP 想象成 AI 应用程序的 USB-C 接口。就像 USB-C 提供了一种…

MySQL 在 CentOS 7 环境下的安装教程

&#x1f31f; 各位看官好&#xff0c;我是maomi_9526&#xff01; &#x1f30d; 种一棵树最好是十年前&#xff0c;其次是现在&#xff01; &#x1f680; 今天来学习Mysql的相关知识。 &#x1f44d; 如果觉得这篇文章有帮助&#xff0c;欢迎您一键三连&#xff0c;分享给更…

WPF的基础设施:XAML基础语法

XAML基础语法 1 控件声明与属性设置1.1 特性语法&#xff08;Attribute Syntax&#xff09;1.2 属性元素语法&#xff08;Property Element Syntax&#xff09;1.3 特殊值标记扩展 2 x:Name与Name的区别3 注释与代码折叠4 实用技巧集合5 常见错误排查 XAML( Extensible Applic…

机器学习笔记【Week3】

一、逻辑回归&#xff08;Logistic Regression&#xff09; 与线性回归的区别&#xff1a; 问题类型输出类型举例回归问题连续实数房价预测、气温预测分类问题离散类别&#xff08;0 或 1&#xff09;是否患病、是否点击广告、是否合格 我们希望构建一个模型&#xff0c;根据…

6.4.2_3最短路径问题_Floyd算法

Floyd弗洛伊德 膜拜大佬&#xff0c;给大佬鞠躬鞠躬鞠躬。。。。。。。。。 Floyd算法 ----解决顶点间的最短路径&#xff1a; 过程&#xff1a; 如下&#xff1a; 初始化(没有中转点)&#xff1a;2个邻接矩阵A和path&#xff0c;第一个是没有中转点的2个顶点之间的最短路径…

uniapp|实现多端图片上传、拍照上传自定义插入水印内容及拖拽自定义水印位置,实现水印相机、图片下载保存等功能

本文以基础视角,详细讲解如何在uni-app中实现图片上传→水印动态编辑→图片下载的全流程功能。 目录 引言应用场景分析(社交媒体、内容保护、企业素材管理等)uniapp跨平台开发优势核心功能实现​图片上传模块多来源支持:相册选择(`uni.chooseImage`)与拍照(`sourceType:…

2021年认证杯SPSSPRO杯数学建模B题(第二阶段)依巴谷星表中的毕星团求解全过程文档及程序

2021年认证杯SPSSPRO杯数学建模 B题 依巴谷星表中的毕星团 原题再现&#xff1a; 依巴谷卫星&#xff08;High Precision Parallax Collecting Satellite&#xff0c;缩写为 Hip-parcos&#xff09;&#xff0c;全称为“依巴谷高精度视差测量卫星”&#xff0c;是欧洲空间局发…

行为型:解释器模式

目录 1、核心思想 2、实现方式 2.1 模式结构 2.2 实现案例 3、优缺点分析 4、适用场景 5、注意事项 1、核心思想 目的&#xff1a;针对某种语言并基于其语法特征创建一系列的表达式类&#xff08;包括终极表达式与非终极表达式&#xff09;​&#xff0c;利用树结构模式…

Redis分布式缓存核心架构全解析:持久化、高可用与分片实战

一、持久化机制&#xff1a;数据安全双引擎 1.1 RDB与AOF的架构设计 Redis通过RDB&#xff08;快照持久化&#xff09;和AOF&#xff08;日志持久化&#xff09;两大机制实现数据持久化。 • RDB架构&#xff1a;采用COW&#xff08;写时复制&#xff09;技术&#xff0c;主进程…

换脸视频FaceFusion3.1.0-附整合包

2025版最强换脸软件FaceFusion来了&#xff08;附整合包&#xff09;超变态的换脸教程 2025版最强换脸软件FaceFusion来了&#xff08;附整合包&#xff09;超变态的换脸教程 整合包地址&#xff1a; 「Facefusion_V3.1.0」 链接&#xff1a;https://pan.quark.cn/s/f71601a920…

论文阅读笔记——Step1X-Edit: A Practical Framework for General Image Editing

Step1X-Edit 论文 当前图像编辑数据集规模小&#xff0c;质量差&#xff0c;由此构建了如下数据构造管线。 高质量三元组数据&#xff08;源图像、编辑指令、目标图像&#xff09;。 主体添加与移除&#xff1a;使用 Florence-2 对专有数据集标注&#xff0c;然后使用 SAM2 进…

使用Python在PyCharm中进行交通工程数据分析的完整流程,包括数据清洗、挖掘、关联、可视化和应用整合等各个阶段

交通工程领域数据分析流程 下面我将详细介绍使用Python在PyCharm中进行交通工程数据分析的完整流程,包括数据清洗、挖掘、关联、可视化和应用整合等各个阶段。 1. 数据准备与清洗 1.1 导入必要库 import pandas as pd import numpy as np import matplotlib.pyplot as plt…

《软件工程》第 2 章 -UML 与 RUP 统一过程

在软件工程领域&#xff0c;UML&#xff08;统一建模语言&#xff09;与 RUP&#xff08;统一过程&#xff09;是进行面向对象软件开发的重要工具和方法。接下来&#xff0c;我们将深入探讨第 2 章的内容&#xff0c;通过案例和代码&#xff0c;帮助大家理解和掌握相关知识。 …

Vue收集表单数据

在 Web 开发中&#xff0c;表单是用户与系统交互的重要方式。无论是注册、登录、提交评论还是其他操作&#xff0c;都需要通过表单获取用户输入的数据。Vue.js 提供了强大的响应式系统和指令&#xff0c;使得表单数据的收集变得简单而高效。本文将详细介绍如何在 Vue 中实现表单…

R基于多元线性回归模型实现汽车燃油效率预测及SHAP值解释项目实战

说明&#xff1a;这是一个机器学习实战项目&#xff08;附带数据代码文档视频讲解&#xff09;&#xff0c;如需数据代码文档视频讲解可以直接到文章最后关注获取。 1.项目背景 在全球环保意识日益增强和技术进步的推动下&#xff0c;汽车燃油效率成为了汽车行业关注的核心指标…

解决Window10上IP映射重启失效的问题

问题 在实际网络搭建过程中&#xff0c;大家有可能会遇到在局域网范围内&#xff0c;在自己本机上搭建一个网站或者应用时&#xff0c;其他设备通过本机的IP地址无法访问的问题,这个问题可以通过设置IP映射来解决&#xff0c;但是通过netsh interface命令设置的IP映射&#xf…