Go 锁扩展
文章目录
- TryLock
- 统计 goroutine数量
- 读写锁
- 读锁
- 写锁
- 常见死锁情况
- 写锁重入
- 写锁中调用读锁
- 循环依赖
TryLock
- 源码中自带的(我的go是 1.20版本)
- TryLock 会尝试获取锁,如果获取不到返回false,并不会进行休眠阻塞(和 Lock的主要区别)
func (m *Mutex) TryLock() bool {old := m.state// 如果被锁或者进入饥饿模式直接放弃if old&(mutexLocked|mutexStarving) != 0 {return false}//竞争锁失败if !atomic.CompareAndSwapInt32(&m.state, old, old|mutexLocked) {return false}if race.Enabled {race.Acquire(unsafe.Pointer(m))}return true
}
统计 goroutine数量
- 由于state 是sync.Mutex的第一个属性,所以可以通过 unsafe.Pointer(&m.Mutex) 获取
- state 含义请看文章 Go锁演进 (第一位代表锁状态,第二位代表唤醒状态,第三位代表饥饿状态,其余代表goroutine数量)
package mainimport ("fmt""sync""sync/atomic""time""unsafe"
)const (mutexLocked = 1 << iota // mutex is lockedmutexWokenmutexStarvingmutexWaiterShift = iota
)type Mutex struct {sync.Mutex
}//获取goroutine数
func (m *Mutex) GetGoroutineNumber() int {//由于state是sync.Mutex的第一个属性,所以可以通过 unsafe.Pointer(&m.Mutex) 获取val := atomic.LoadUint32((*uint32)(unsafe.Pointer(&m.Mutex)))return int(val&mutexLocked + val>>mutexWaiterShift)
}var m Mutexfunc main() {for i := 0; i < 10; i++ {go func() {m.Lock()time.Sleep(2 * time.Second)m.Unlock()}()}go func() {ticker := time.NewTicker(1 * time.Second)defer ticker.Stop()for {select {case <-ticker.C:fmt.Println(m.GetGoroutineNumber())}}}()time.Sleep(30 * time.Second)
}
读写锁
- 读写锁采用的是写锁优先的模式
- 当获取写锁时(T1时刻),如果在T1之前已经有goroutine获取到读锁, 写锁进入阻塞等待,等待T1之前的读锁全部释放后再唤醒。T1之后的读锁会全部阻塞进入等待,等待写锁释放在执行读锁
读锁
- readerCount 为负数代表有写锁等待
- 有写锁等待的情况下, readerCount 为负数,readerWait 为正 (看读锁的 Lock 逻辑)
- 在写锁等待的情况下, readerCount + 1, readerWait -1
type RWMutex struct {w Mutex // held if there are pending writerswriterSem uint32 // semaphore for writers to wait for completing readersreaderSem uint32 // semaphore for readers to wait for completing writersreaderCount int32 // number of pending readersreaderWait int32 // number of departing readers
}func (rw *RWMutex) RLock() {//竞态忽略if race.Enabled {_ = rw.w.staterace.Disable()}//如果当前的reader等待数 +1 < 0,说明有写操作需要获取锁,阻塞读,等待唤醒if atomic.AddInt32(&rw.readerCount, 1) < 0 {runtime_SemacquireMutex(&rw.readerSem, false, 0)}//竞态忽略if race.Enabled {race.Enable()race.Acquire(unsafe.Pointer(&rw.readerSem))}
}func (rw *RWMutex) RUnlock() {//竞态忽略if race.Enabled {_ = rw.w.staterace.ReleaseMerge(unsafe.Pointer(&rw.writerSem))race.Disable()}if r := atomic.AddInt32(&rw.readerCount, -1); r < 0 {//有写等待rw.rUnlockSlow(r)}//竞态忽略if race.Enabled {race.Enable()}
}func (rw *RWMutex) rUnlockSlow(r int32) {//重复解锁情况下if r+1 == 0 || r+1 == -rwmutexMaxReaders {race.Enable()throw("sync: RUnlock of unlocked RWMutex")}if atomic.AddInt32(&rw.readerWait, -1) == 0 {//如果写之前的读都完成了。那么写可以开始干活了runtime_Semrelease(&rw.writerSem, false, 1)}
}
写锁
- 写锁获取锁之前,发现还有读锁,会将 readerCount - rwmutexMaxReaders 得到一个 负值 readerCount代表写锁等待
- 写锁释放后,会将 readerCount + rwmutexMaxReaders 变成写锁等待状态
func (rw *RWMutex) Lock() {//竞态忽略if race.Enabled {_ = rw.w.staterace.Disable()}//写锁复用的sync.Mutexrw.w.Lock()//变成负的来表示写操作要入场了 r := atomic.AddInt32(&rw.readerCount, -rwmutexMaxReaders) + rwmutexMaxReaders//读还在占用锁,写还是需要等待,维护写操作需要等待的读操作数量(readerWait)if r != 0 && atomic.AddInt32(&rw.readerWait, r) != 0 {runtime_SemacquireMutex(&rw.writerSem, false, 0)}//竞态忽略if race.Enabled {race.Enable()race.Acquire(unsafe.Pointer(&rw.readerSem))race.Acquire(unsafe.Pointer(&rw.writerSem))}
}func (rw *RWMutex) Unlock() {//竞态忽略if race.Enabled {_ = rw.w.staterace.Release(unsafe.Pointer(&rw.readerSem))race.Disable()}//重复解锁r := atomic.AddInt32(&rw.readerCount, rwmutexMaxReaders)if r >= rwmutexMaxReaders {race.Enable()throw("sync: Unlock of unlocked RWMutex")}//把写期间的goroutine给他调用了for i := 0; i < int(r); i++ {runtime_Semrelease(&rw.readerSem, false, 0)}// Allow other writers to proceed.rw.w.Unlock()//竞态忽略if race.Enabled {race.Enable()}
}
常见死锁情况
写锁重入
读写锁的的写锁是基于 sync.Mutex
package mainimport ("sync"
)var s sync.RWMutexfunc main() {s.Lock()s.Lock()
}
写锁中调用读锁
在 Rlock 后面的 Lock 会阻塞等待 RUnlock,而 RUnlock又被 Lock阻塞,故此死锁
s.RLock()s.Lock()s.RUnlock()s.Unlock()
循环依赖
- 16 行程序开始获取到读锁(第一个读)
- 27 行程序 1秒后写锁入场,写锁依赖 16行中的Rlock(等待第一个读释放锁)
- 18 行程序2秒后读锁入场,读锁依赖27行的 Lock(等待写获取锁并释放)
- 16 行程序想解锁,依赖 18行的读锁 (等待第二个锁先释放(第二个读是在写锁等待之后入场,所以会阻塞),然后才能释放第一个锁)
上面就是下面代码死锁流程
package mainimport ("fmt""sync""time"
)var s sync.RWMutex
var w sync.WaitGroupfunc main() {w.Add(3)go func() {s.RLock()time.Sleep(2 * time.Second)s.RLock()w.Done()s.RUnlock()w.Done()s.RUnlock()}()go func() {time.Sleep(1 * time.Second)s.Lock()w.Done()s.Unlock()}()w.Wait()fmt.Println("凉凉")
}
相关文章:
Go 锁扩展
文章目录 TryLock统计 goroutine数量读写锁读锁写锁常见死锁情况写锁重入写锁中调用读锁循环依赖 TryLock 源码中自带的(我的go是 1.20版本)TryLock 会尝试获取锁,如果获取不到返回false,并不会进行休眠阻塞(和 Lock的主要区别) func (m *Mutex) TryLo…...

Docker的简介及安装
[shouce]http://shouce.jb51.net/docker_practice/栾一峰菜鸟教程参考文献 1 环境配置的难题 软件开发最大的麻烦事之一,就是环境配置。用户计算机的环境都不相同,你怎么知道自家的软件,能在那些机器跑起来? 用户必须保证两件事…...

安卓核心板的不同核心规格及架构介绍
安卓核心板是将核心功能封装的一块电子主板,集成芯片、存储器和功放器件等,并提供标准接口的芯片。 其特点: ● 能跑 Android 等操作系统 强大的功能及丰富的接口 支持 LCD/TP,Audio,Camera,Video&#…...

flume1.11.0安装部署
1、准备安装包apache-flume-1.11.0-bin.tar.gz; 上传; 2、安装flume-1.11.0; 解压; tar -zxvf apache-flume-1.11.0-bin.tar.gz -C /opt/server 进入conf目录,修改flume-env.sh,配置JAVA_HOME;…...

通过wordpress 自定义主题的额外CSS删除指定区块
最近用wordpress建站,想要删除指定区块,发现相关的教程蛮少的,作为小白的我搜了相关教程,好像没找到,只能自己慢慢摸索了,看了很多,终于尝试实现了,特记录下,免得自己忘了…...

Rokid Jungle--Max pro
介绍和功能开发 YodaOS-Master操作系统:以交换计算为核心,实现单目SLAM空间交互,具有高精度、实时性和稳定性。发布UXR2.0SDK,为构建空间内容提供丰富的开发套件 多模态交互 算法原子化 多种开发工具协同 多生态支持 骁龙XR2…...

【LeetCode算法系列题解】第61~65题
CONTENTS LeetCode 61. 旋转链表(中等)LeetCode 62. 不同路径(中等)LeetCode 63. 不同路径 II(中等)LeetCode 64. 最小路径和(中等)LeetCode 65. 有效数字(困难ÿ…...

MATLAB中fillmissing函数用法
目录 语法 说明 示例 包含 NaN 值的向量 由 NaN 值组成的矩阵 插入缺失数据 使用移动中位数方法 使用自定义填充方法 包含缺失端点的矩阵 包含多个数据类型的表 fillmissing函数的功能是填充缺失的条目。 语法 F fillmissing(A,constant,v) F fillmissing(A,meth…...

电脑同时连接有线和无线网络怎么设置网络的优先级
电脑同时连接有线和无线网络怎么设置网络的优先级: 我们知道在 笔记本电脑系统 中,可以通过有线或无线网络进行联网。如果电脑在有线网络和无线网络同时存在的情况,应该怎么设置有线网络优先连接呢?对此我们提供下面的方法可以让电脑在有Wi…...

el-form表单动态校验(场景: 输入框根据单选项来动态校验表单 没有选中的选项就不用校验)
el-form表单动态校验 el-form常规校验方式: // 结构部分 <el-form ref"form" :model"form" :rules"rules"><el-form-item label"活动名称: " prop"name" required><el-input v-model"form.name" /…...
Java 数据结构与算法应该如何学习?
学习数据结构是计算机科学和软件工程领域中的重要基础知识之一。掌握数据结构对于编写高效、可扩展和可维护的代码至关重要。 1、掌握基本概念 首先,你需要掌握数据结构的基本概念。了解不同类型的数据结构,如数组、链表、栈、队列、树、图等ÿ…...

力扣(LeetCode)算法_C++——有效的数独
请你判断一个 9 x 9 的数独是否有效。只需要 根据以下规则 ,验证已经填入的数字是否有效即可。 数字 1-9 在每一行只能出现一次。 数字 1-9 在每一列只能出现一次。 数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。(请参考示例图) …...

制造企业如何优化物料控制?
导 读 ( 文/ 2127 ) 物料控制是指对制造过程中所涉及的物料流动和库存进行有效管理和控制的过程。它包括物料需求计划、供应商管理、物料采购、物料接收和入库、物料库存管理以及物料发放和使用等关键环节。通过精确的物料需求计划和库存管理,物料控制可以确保物料供…...

《Go语言在微服务中的崛起:为什么Go是下一个后端之星?》
🌷🍁 博主猫头虎🐅🐾 带您进入 Golang 语言的新世界✨✨🍁 🦄 博客首页——🐅🐾猫头虎的博客🎐 🐳 《面试题大全专栏》 🦕 文章图文并茂…...

因为axios请求后端,接收不到token的问引出的问题
vue axios请求后端接受不到token的问题。 相关概念 什么是跨域? 跨域指的是在浏览器环境下,当发起请求的域(或者网站)与请求的资源所在的域之间存在协议、主机或端口中的任何一个条件不同的情况。换句话说,只要协议、…...

Stable Diffusion 免费升级 SDXL 1.0,哪些新特性值得关注?体验如何?5 分钟带你体验!
一、引言 7 月 26 日,Stability AI 发布了 SDXL 1.0,号称目前为止,最厉害的开放式图像生成大模型。 它到底有没有网上说的那么炸裂?真的已经实现了像 midjourney 一样 靠嘴出图 的功能吗?相对于之前的版本,…...

【广州华锐互动】煤矿设备AR远程巡检系统实现对井下作业的远程监控和管理
煤矿井下作业环境复杂,安全隐患较多。传统的巡检方式存在诸多弊端,如巡检人员难以全面了解井下情况,巡检效率低下,安全隐患难以及时发现和整改等。为了解决这些问题,提高煤矿安全生产水平,越来越多的企业开…...
C语言与Java语言传输数据 需要转位
在Java语言中,可以通过将整数反转并修改字节顺序来实现低位转高位的转换。下面是一个示例代码,可以将一个整数从低位转高位: public static int toHH(int n) {byte[] bytes ByteBuffer.allocate(4).putInt(n).array();for (int i 0; i <…...
Framework开发——系统默认语言修改
Android 系统原版默认的语言为英文,但是对于中国大陆 Android 产品厂商来说,我们定制系统可能需要用户一开机就是简体中文。所以把 Android 系统出厂设置为简体中文对于 Android 系统产品化非常重要,我们可以通过修改系统属性来达到默认语言的作用。本文主要是在 Android 11…...

浅谈原型链
一.在掌握原型链之前首先要了解这三点 1.每个函数都有prototype这个属性我们称为原型对象 2.每个对象都有__proto__这个属性 3.对象的__proto__可以访问原型对象上的方法和变量,如果访问不了,就会向上进行查找,直到找不到为止,会出现报错的情况l。 二.例子 1.代码: let arr …...

Mybatis逆向工程,动态创建实体类、条件扩展类、Mapper接口、Mapper.xml映射文件
今天呢,博主的学习进度也是步入了Java Mybatis 框架,目前正在逐步杨帆旗航。 那么接下来就给大家出一期有关 Mybatis 逆向工程的教学,希望能对大家有所帮助,也特别欢迎大家指点不足之处,小生很乐意接受正确的建议&…...
Qwen3-Embedding-0.6B深度解析:多语言语义检索的轻量级利器
第一章 引言:语义表示的新时代挑战与Qwen3的破局之路 1.1 文本嵌入的核心价值与技术演进 在人工智能领域,文本嵌入技术如同连接自然语言与机器理解的“神经突触”——它将人类语言转化为计算机可计算的语义向量,支撑着搜索引擎、推荐系统、…...

从零开始打造 OpenSTLinux 6.6 Yocto 系统(基于STM32CubeMX)(九)
设备树移植 和uboot设备树修改的内容同步到kernel将设备树stm32mp157d-stm32mp157daa1-mx.dts复制到内核源码目录下 源码修改及编译 修改arch/arm/boot/dts/st/Makefile,新增设备树编译 stm32mp157f-ev1-m4-examples.dtb \stm32mp157d-stm32mp157daa1-mx.dtb修改…...

MySQL 8.0 OCP 英文题库解析(十三)
Oracle 为庆祝 MySQL 30 周年,截止到 2025.07.31 之前。所有人均可以免费考取原价245美元的MySQL OCP 认证。 从今天开始,将英文题库免费公布出来,并进行解析,帮助大家在一个月之内轻松通过OCP认证。 本期公布试题111~120 试题1…...
高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数
高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数 在软件开发中,单例模式(Singleton Pattern)是一种常见的设计模式,确保一个类仅有一个实例,并提供一个全局访问点。在多线程环境下,实现单例模式时需要注意线程安全问题,以防止多个线程同时创建实例,导致…...

stm32wle5 lpuart DMA数据不接收
配置波特率9600时,需要使用外部低速晶振...
es6+和css3新增的特性有哪些
一:ECMAScript 新特性(ES6) ES6 (2015) - 革命性更新 1,记住的方法,从一个方法里面用到了哪些技术 1,let /const块级作用域声明2,**默认参数**:函数参数可以设置默认值。3&#x…...
跨平台商品数据接口的标准化与规范化发展路径:淘宝京东拼多多的最新实践
在电商行业蓬勃发展的当下,多平台运营已成为众多商家的必然选择。然而,不同电商平台在商品数据接口方面存在差异,导致商家在跨平台运营时面临诸多挑战,如数据对接困难、运营效率低下、用户体验不一致等。跨平台商品数据接口的标准…...

goreplay
1.github地址 https://github.com/buger/goreplay 2.简单介绍 GoReplay 是一个开源的网络监控工具,可以记录用户的实时流量并将其用于镜像、负载测试、监控和详细分析。 3.出现背景 随着应用程序的增长,测试它所需的工作量也会呈指数级增长。GoRepl…...
基于Uniapp的HarmonyOS 5.0体育应用开发攻略
一、技术架构设计 1.混合开发框架选型 (1)使用Uniapp 3.8版本支持ArkTS编译 (2)通过uni-harmony插件调用原生能力 (3)分层架构设计: graph TDA[UI层] -->|Vue语法| B(Uniapp框架)B --&g…...