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…

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;这些头部都是…

修改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…

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…

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

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

yolov8 tracking编码为web 和 rtsp流输出

1 基础工作 打开cmd 输入 conda env list 输入 conda activate py38 查看 nvidia-smi 查看 nvcc&#xff0c;如下图所示 cuda为11.7 &#xff0c;为确认可以查看program files 下面的cuda 安装&#xff0c;看到11.7 就行了&#xff0c;读者可以自行确认自己的版本。 查看nvid…

【基础】【Python网络爬虫】【2.请求与响应】常用请求报头和常用响应方法

Python网络爬虫基础 爬虫基础请求与相应HTTP/HTTPS 协议HTTP/HTTPS的优缺点HTTP 的缺点HTTPS的优点 请求与响应概述请求请求目标&#xff08;url&#xff09;请求体&#xff08;response&#xff09;常用的请求报头查看请求体&#xff08;requests 模块&#xff09; 响应HTTP响…

PS实现圆角矩形选取

在我们日常的工作当中,经常需要做圆角矩形的图片,因为圆角的图片给人的感觉比较圆润,漂亮,但是PS在这方面显然比较弱智,因此如何做圆角矩形选择,这里给出了两个方法,希望能帮到大家。 进入正题(方法一): 第一步-PS 右上角-打开一张图片 第二步-左列-选择矩形-选择目…

2024新年快乐

今天就不讲题解了&#xff0c;2024年&#xff0c;祝所有博客身体健康&#xff0c;粉丝越多&#xff0c;点赞越高&#xff01;&#xff01;&#xff01; #include<bits/stdc.h> using namespace std; int main(){cout<<"新年快乐!!";return 0&#xff1b;…

【小沐学Python】Python实现免费天气预报获取(OpenWeatherMap)

文章目录 1、简介1.1 工具简介1.2 费用1.3 注册1.4 申请key 2、接口说明2.1 One Call 3.02.2 Current Weather and Forecasts collection2.2.1 API 调用2.2.2 API 参数 2.3 Historical Weather collection2.4 Weather Maps collection2.5 Other weather APIs 3、接口测试3.1 例…

JavaFX:MVC模式学习01-使用PropertyValueFactory将模型与视图绑定

PropertyValueFactory类是“TableColumn cell value factory”,绑定创建列表中的项。示例如下&#xff1a; TableColumn<Person,String> firstNameCol new TableColumn<Person,String>("First Name");firstNameCol.setCellValueFactory(new PropertyVal…

IIS服务器发布PHP网站

IIS服务器&#xff0c;相信开发者都不会陌生&#xff0c;它的英文全称是Internet Information Services&#xff0c;是由微软公司提供的基于运行Microsoft Windows的互联网基本服务&#xff0c;常用于Windows系统的Web项目部署&#xff0c;本篇以PHP项目为例&#xff0c;讲解如…