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 …...
变量 varablie 声明- Rust 变量 let mut 声明与 C/C++ 变量声明对比分析
一、变量声明设计:let 与 mut 的哲学解析 Rust 采用 let 声明变量并通过 mut 显式标记可变性,这种设计体现了语言的核心哲学。以下是深度解析: 1.1 设计理念剖析 安全优先原则:默认不可变强制开发者明确声明意图 let x 5; …...
Cesium1.95中高性能加载1500个点
一、基本方式: 图标使用.png比.svg性能要好 <template><div id"cesiumContainer"></div><div class"toolbar"><button id"resetButton">重新生成点</button><span id"countDisplay&qu…...
智能在线客服平台:数字化时代企业连接用户的 AI 中枢
随着互联网技术的飞速发展,消费者期望能够随时随地与企业进行交流。在线客服平台作为连接企业与客户的重要桥梁,不仅优化了客户体验,还提升了企业的服务效率和市场竞争力。本文将探讨在线客服平台的重要性、技术进展、实际应用,并…...
WordPress插件:AI多语言写作与智能配图、免费AI模型、SEO文章生成
厌倦手动写WordPress文章?AI自动生成,效率提升10倍! 支持多语言、自动配图、定时发布,让内容创作更轻松! AI内容生成 → 不想每天写文章?AI一键生成高质量内容!多语言支持 → 跨境电商必备&am…...
Element Plus 表单(el-form)中关于正整数输入的校验规则
目录 1 单个正整数输入1.1 模板1.2 校验规则 2 两个正整数输入(联动)2.1 模板2.2 校验规则2.3 CSS 1 单个正整数输入 1.1 模板 <el-formref"formRef":model"formData":rules"formRules"label-width"150px"…...
搭建DNS域名解析服务器(正向解析资源文件)
正向解析资源文件 1)准备工作 服务端及客户端都关闭安全软件 [rootlocalhost ~]# systemctl stop firewalld [rootlocalhost ~]# setenforce 0 2)服务端安装软件:bind 1.配置yum源 [rootlocalhost ~]# cat /etc/yum.repos.d/base.repo [Base…...
Python Einops库:深度学习中的张量操作革命
Einops(爱因斯坦操作库)就像给张量操作戴上了一副"语义眼镜"——让你用人类能理解的方式告诉计算机如何操作多维数组。这个基于爱因斯坦求和约定的库,用类似自然语言的表达式替代了晦涩的API调用,彻底改变了深度学习工程…...
怎么让Comfyui导出的图像不包含工作流信息,
为了数据安全,让Comfyui导出的图像不包含工作流信息,导出的图像就不会拖到comfyui中加载出来工作流。 ComfyUI的目录下node.py 直接移除 pnginfo(推荐) 在 save_images 方法中,删除或注释掉所有与 metadata …...
十九、【用户管理与权限 - 篇一】后端基础:用户列表与角色模型的初步构建
【用户管理与权限 - 篇一】后端基础:用户列表与角色模型的初步构建 前言准备工作第一部分:回顾 Django 内置的 `User` 模型第二部分:设计并创建 `Role` 和 `UserProfile` 模型第三部分:创建 Serializers第四部分:创建 ViewSets第五部分:注册 API 路由第六部分:后端初步测…...
消息队列系统设计与实践全解析
文章目录 🚀 消息队列系统设计与实践全解析🔍 一、消息队列选型1.1 业务场景匹配矩阵1.2 吞吐量/延迟/可靠性权衡💡 权衡决策框架 1.3 运维复杂度评估🔧 运维成本降低策略 🏗️ 二、典型架构设计2.1 分布式事务最终一致…...
