golang锁源码【只有关键逻辑】

条件锁
type Cond struct {L Lockernotify  notifyList
}
type notifyList struct {wait   uint32 //表示当前 Wait 的最大 ticket 值notify uint32 //表示目前已唤醒的 goroutine 的 ticket 的最大值lock   uintptr // key field of the mutexhead   unsafe.Pointer //链表头tail   unsafe.Pointer  //链表尾
}

相关函数

func (c *Cond) Wait() {// 获取当前ticket,即wait的值,实际是atomic.Xadd(&l.wait, 1) - 1,这样wait原子加1,t := runtime_notifyListAdd(&c.notify)// 注意这里,必须先解锁,因为 runtime_notifyListWait 要切走 goroutine// 所以这里要解锁,要不然其他 goroutine 没法获取到锁了//功能是将当前 goroutine 插入到 notifyList 链表c.L.Unlock()runtime_notifyListWait(&c.notify, t)// 这里已经唤醒了,因此需要再度锁上c.L.Lock()
}
func (c *Cond) Signal() {runtime_notifyListNotifyOne(&c.notify)
}
//notifyList 是一个链表结构,我们为何不直接取链表最头部唤醒呢
//因为 notifyList 会有乱序的可能,获取 ticket 和加入 notifyList
//是两个独立的行为,并发操作有可能的乱序,大部分场景下比较一两次之后就会很快停止
func notifyListNotifyOne(l *notifyList) {***for p, s := (*sudog)(nil), l.head; s != nil; p, s = s, s.next {if s.ticket == t {n := s.nextif p != nil {p.next = n} else {l.head = n}if n == nil {l.tail = p}s.next = nilreturn}}***
}
func (c *Cond) Broadcast() {runtime_notifyListNotifyAll(&c.notify)
}
//唤醒所有等待的goruntine
fuc notifyListNotifyAll(l *notifyList) {// Go through the local list and ready all waiters.for s != nil {next := s.nexts.next = nilreadyWithTime(s, 4)s = next}
}
互斥锁 mutex

自旋:新goroutine或被唤醒的goroutine首次获取不到锁,就会自旋(spin,通过循环不断尝试,在runtime实现的)的方式尝试检查锁,不需要上下文切换,提供性能。
饥饿模式:高并发情况下,新来的g一直自旋,waiter可能悲剧地获取不到锁。故waiter获取不到锁的时间超过阈值1毫秒,它会被插入到队列的前面,这个Mutex进入饥饿模式

在饥饿模式下,Mutex的拥有者将直接把锁交给队列最前面的waiter。新的g不会尝试获取锁,不抢也不spin,乖乖地加入到等待队列的尾部。

如果拥有Mutex的waiter发现下面两种情况的其中之一,它就会把这个Mutex转换成正常模式:

  • 此waiter已经是队列中的最后一个waiter了,没有其它的等待锁的goroutine了;
  • 此waiter的等待时间小于1毫秒。
// A Locker represents an object that can be locked and unlocked.
type Locker interface { //锁接口Lock()Unlock()
}
type Mutex struct {//互斥锁实例state int32 //状态sema  uint32 //信号量
}
const (mutexLocked = 1 << iota //0x0000 0001mutexWoken //0x0000 0010mutexStarving //0x0000 0100mutexWaiterShift = iota  3 //等待者数量的标志,最多可以阻塞2^29个goroutine。starvationThresholdNs = 1e6
)

mutexLocked代表锁的状态,如果为1代表已加锁
mutexWoken代表是否唤醒,如果为 1代表已唤醒
mutexStarving代表是否处于饥饿模式,如果为1 代表是
mutexWaiterShift 值是3,有人会问这里为什么不左移,而是直接是3,这是因为3这个会有1<<mutexWaiterShift代表1个waiter数量,也有右移代表获取mutex上面的waiter数量。
starvationThresholdNs代表判断饥饿模式的时间,如果等待时间超过这个时间就判断为饥饿
在这里插入图片描述

func (m *Mutex) Lock() {// Slow path (outlined so that the fast path can be inlined)m.lockSlow() //补贴源码,看图了
}

在这里插入图片描述

解锁,别贴代码,上图
在这里插入图片描述

读写锁

type RWMutex struct {w           Mutex  writerSem   uint32 // 用于writer等待,wait读完成排队的信号量readerSem   uint32 // 用于reader等待,wait写完成排队的信号量readerCount atomic.Int32  // 读锁的计数器readerWait  atomic.Int32 // 有写锁时,等待读锁释放的数量
}
const rwmutexMaxReaders = 1 << 30 //go支持的最高加读锁的数量
func (rw *RWMutex) RLock() {// reader计数器+1// 如果小于0则表明有写锁在占有if rw.readerCount.Add(1) < 0 {// 等待写锁释放,runtime_SemacquireRWMutexR(&rw.readerSem, false, 0)}
}
func (rw *RWMutex) RUnlock() {// reader计数器-1,// 如果小于0,说明有读锁在等待if r := rw.readerCount.Add(-1); r < 0 {rw.rUnlockSlow(r)}
}
func (rw *RWMutex) rUnlockSlow(r int32) {// 等待的读书数量-1,等于0说明已经没有等待的读锁if rw.readerWait.Add(-1) == 0 {// 最后一名读锁了,唤醒写锁runtime_Semrelease(&rw.writerSem, false, 1)}
}
func (rw *RWMutex) Lock() {//  与写锁竞争,获取互斥锁rw.w.Lock()// 把readerCount- rwmutexMaxReaders,表示有写锁r := rw.readerCount.Add(-rwmutexMaxReaders) + rwmutexMaxReaders// 等待读锁数量readerwait+readerCount的数量,等待读锁完成.if r != 0 && rw.readerWait.Add(r) != 0 {//等待写锁信号量runtime_SemacquireRWMutex(&rw.writerSem, false, 0)}
}
func (rw *RWMutex) Unlock() {// 告诉读锁,写锁已完成,可以读了.r := rw.readerCount.Add(rwmutexMaxReaders)//唤醒所有读锁for i := 0; i < int(r); i++ {runtime_Semrelease(&rw.readerSem, false, 0)}// 允许其他写锁获取锁rw.w.Unlock()
}

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

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

相关文章

论文解读:Coordinate Attention for Efficient Mobile Network Design(CVPR2021)

论文前言 原理其实很简单&#xff0c;但是论文作者说得很抽象&#xff0c;时间紧的建议直接看3.1中原理简述CBMA、原理简述CBMA以及3.2中原理简述coordinate attention block即可。 Abstract 最近关于mobile network设计的研究已经证明了通道注意(例如&#xff0c;the Squee…

java中file类常用方法举例说明

java中file类常用方法举例说明 当使用 java.io.File 类时&#xff0c;以下是一些常用方法的举例说明&#xff1a; 创建文件或目录&#xff1a; // 使用路径名创建File实例 File file new File("C:\\Users\\UserName\\Documents\\example.txt");// 使用父路径和子路…

JAVA-ArrayList的相关坑

ArrayList的asList()方法 Arrays.asList()方法&#xff0c;本质是调用了Arrays的一个静态内部类&#xff0c;实现了AbstractList接口&#xff0c;这个方法是重写了AbstractList的方法&#xff0c;但是这个asList()大小是固定的&#xff0c;当我们使用add方法时会调用父类Abstr…

roslaunch格式

The roslaunch package comes with roslaunch tool as well as several support tools to assist in the process of launching ROS Nodes. 目录 roslaunch Launch syntaxPassing in argsNon-launch optionsInternal-use only optionsEnvironment Variables (advanced users)…

23. 一维数组

写在前面&#xff1a; 今天是2023年12月31日&#xff0c;也是整个2023年的最后一天。我在CSDN上只有短短几个月的时光&#xff0c;但非常感谢大家的支持&#xff0c;作为一名刚刚大一的大学生呢&#xff0c;学习编程&#xff0c;学习写博客是很重要的事&#xff0c;所以在新的…

翻页的电子画册如何制作

​在过去&#xff0c;一本精美的画册往往需要大量的人力物力去印刷、装帧、运输。而现在&#xff0c;只需一台电脑、一个网址和一个创意&#xff0c;就可以轻松制作出一本电子画册。这种变化不仅降低了成本&#xff0c;还带来了更多的便利性和灵活性。 首先&#xff0c;你需要选…

网络故障排查和流量分析利器-Tcpdump命令

Tcpdump是一个在Unix/Linux系统上广泛使用的命令行网络抓包工具。它能够捕获经过网络接口的数据包&#xff0c;并将其以可读的格式输出到终端或文件中。Tcpdump是一个强大的命令行工具&#xff0c;能够捕获和分析网络数据包&#xff0c;为网络管理员和安全专业人员提供了深入了…

【网络面试(6)】IP协议对网络包的转发

在前面的博客中&#xff0c;我们提到过&#xff0c;网络传输的报文是有真实的数据包和一些头部组成&#xff0c;目前我们了解的头部就有TCP头、IP头、MAC头&#xff0c;而且这三个头部信息都是在应用程序委托给协议栈之后&#xff0c;被写入的相关信息&#xff0c;这些头部都是…

Leetcode4-唯一元素的和(1748)

1、题目 &#xff08;1748&#xff09;给你一个整数数组 nums 。数组中唯一元素是那些只出现 恰好一次 的元素。 请你返回 nums 中唯一元素的 和 。 示例 1&#xff1a; 输入&#xff1a;nums [1,2,3,2] 输出&#xff1a;4 解释&#xff1a;唯一元素为 [1,3] &#xff0c;…

修改jenkins的目录(JENKINS_HOME)

默认JENKINS_HOME是/var/lib/jenkins/ 现要修改为/home/jenkins_data/jenkins 最开始 sudo cp -a /var/lib/jenkins/ /home/jenkins_data/ 然后如下操作&#xff1a; 1、首先 /etc/sysconfig/jenkins&#xff1a;jenkins配置文件&#xff0c;“端口”&#xff0c;“JENKIN…

9、python-闭包

简介 1.闭包&#xff0c;又称闭包函数或者闭合函数&#xff0c;其实和嵌套函数类似 2.不同之处在于&#xff0c;闭包中外部函数返回的不是一个具体的值&#xff0c;而是一个函数 3.一般情况下&#xff0c;返回的函数会赋值给一个变量&#xff0c;这个变量可以在后面被继续执行…

yolov8知识蒸馏代码详解:支持logit和feature-based蒸馏

文章目录 1. 知识蒸馏理论2. yolov8 蒸馏代码应用2.1 环境配置2.2 训练模型(1) 训练教师模型(2) 训练学生模型baseline(3) 蒸馏训练3. 知识蒸馏代码详解3.1 蒸馏参数设置3.2 蒸馏损失代码讲解3.2.1 Feature based loss3.2.1 Logit loss3.3 获取蒸馏的feature map及channels

08-接口文档管理工具-项目集成knife4j__ev

2、knife4j快速入门 2.1 knife4j介绍 knife4j是为Java MVC框架集成Swagger生成Api文档的增强解决方案,前身是swagger-bootstrap-ui,取名kni4j是希望它能像一把匕首一样小巧,轻量,并且功能强悍! gitee地址&#xff1a;knife4j: Knife4j是一个集Swagger2 和 OpenAPI3为一体的增…

第二十六周:学习笔记

第二十六周&#xff1a;学习笔记 摘要Abstract全卷积网络 FCN1. CNN 与 FCN2. 全连接层 --> 成卷积层3. FCN的缺点 摘要 全卷积神经网络&#xff08;Fully Convolutional Network&#xff0c;FCN&#xff09;是一种用于图像分割和语义分割任务的深度学习模型。与传统的卷积…

使用python快速开发与PDF文档对话的Gemini聊天机器人

检索增强生成(Retrieval-augmented generation&#xff0c;RAG)使得我们可以让大型语言模型(LLMs)访问外部知识库数据(如pdf,word、text等)&#xff0c;从而让人们可以更加方便的通过LLM来学习外部数据的知识。今天我们将利用之前学习到的RAG方法&#xff0c;谷歌Gemini模型和l…

费曼学习法应用:谈自私和教育的引导

今天这个还是来源于我和九迁的对话&#xff0c;起因是中午吃饭的时候&#xff0c;九迁在学校与班主任老师和数学老师对话中带来的思考。 先听音频&#xff1a; 对话内容&#xff08;以下内容可以边听边看&#xff0c;属于语音转换过来的文字&#xff0c;最后有个总结&#xff0…

U4_3 语法分析-自底向上分析-LR0/LR1/SLR分析

文章目录 一、LR分析法1、概念2、流程3、LR分析器结构及分析表构造1&#xff09;结构2&#xff09;一些概念 二、LR(0)分析法1、流程2、分析动作1&#xff09;移近2&#xff09;归约(reduce) 3、总结1&#xff09;LR分析器2&#xff09;构造DFA3&#xff09;构造LR(0)的方法(三…

基于Vite创建简单Vue3工程

首先安装node.js环境&#xff0c;没有node.js环境&#xff0c;便没有npm命令。 1、Vue3创建执行命令 D:\TABLE\test>npm create vuelatestVue.js - The Progressive JavaScript Framework√ 请输入项目名称&#xff1a; ... vue_test √ 是否使用 TypeScript 语法&#xff…

UntiyShader(五)属性、内置文件和变量

目录 一、如何使用属性 例子 ShaderLab中的属性的类型和Cg中的变量的类型之间的匹配关系

网络安全-真实ip获取伪造与隐藏挖掘

目录 真实ip获取应用层网络层网络连接TOAproxy protocol ip伪造应用层网络层TOA攻击proxy protocol 隐藏代理 挖掘代理多地ping历史DNS解析记录国外主机解析域名网站RSS订阅网络空间搜索引擎 总结参考 本篇文章学习一下如何服务如何获取真实ip&#xff0c;隐藏自己的ip&#xf…