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

news2025/1/23 22:26:49
条件锁
type Cond struct {
	L Locker
	notify  notifyList
}
type notifyList struct {
	wait   uint32 //表示当前 Wait 的最大 ticket 值
	notify uint32 //表示目前已唤醒的 goroutine 的 ticket 的最大值
	lock   uintptr // key field of the mutex
	head   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.next
            if p != nil {
                p.next = n
            } else {
                l.head = n
            }
            if n == nil {
                l.tail = p
            }
            s.next = nil
            return
        }
    }
    ***
}

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.next
		s.next = nil
		readyWithTime(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 0001
    mutexWoken //0x0000 0010
    mutexStarving //0x0000 0100
    mutexWaiterShift = 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.coloradmin.cn/o/1347217.html

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

相关文章

单片机原理及应用:开关控制LED多种点亮模式

从这篇文章开始&#xff0c;我们不再只研究单一的外设工作&#xff0c;而是将LED、数码管、开关、按键搭配在一起研究&#xff0c;这篇文章主要介绍LED和开关能擦出怎样的火花&#xff0c;同时也介绍一些函数封装的知识。 由于开关有闭合与打开两种状态&#xff0c;LED有左移流…

机器学习---随机森林宫颈癌分类

1. 宫颈癌分类 from sklearn import tree from sklearn.ensemble import RandomForestClassifier from sklearn.model_selection import train_test_split from sklearn.model_selection import GridSearchCV from sklearn.pipeline import Pipeline from sklearn.preprocessi…

【操作系统xv6】学习记录1

前置说明&#xff1a; git-v9版本&#xff1a;git clone https://github.com/mit-pdos/xv6-public/tree/xv6-rev9 bili:https://www.bilibili.com/video/BV15r4y1z75F 深圳大学罗秋明老师的课程 我自己用的wsl2的ubuntu18 无桌面版本 make qemu-nox bug 起初在双系统的ubuntu…

go 源码解读 sync.RWMutex

sync.RWMutex 简介源码结构RLockRUnlockUnlockgo 运行时方法 简介 简述sync包中读写锁的源码。 &#xff08;go -version 1.21&#xff09; 读写锁&#xff08;RWMutex&#xff09;是一种并发控制机制&#xff0c;用于在多个 goroutine 之间对共享资源进行读写操作。它提供了…

10|记忆:通过Memory记住客户上次买花时的对话细节

10&#xff5c;记忆&#xff1a;通过Memory记住客户上次买花时的对话细节 在默认情况下&#xff0c;无论是 LLM 还是代理都是无状态的&#xff0c;每次模型的调用都是独立于其他交互的。也就是说&#xff0c;我们每次通过 API 开始和大语言模型展开一次新的对话&#xff0c;它…

Servlet中常用的三大API

HttpServlet 我们写 Servlet 代码的时候&#xff0c;首先第一步就是先创建类&#xff0c;继承自 HttpServlet&#xff0c;并重写其中的某些方法。我们实际开发的时候主要重写 doXXX 方法&#xff0c;很少会重写 init / destory / service。 因为这一些方法的调用时机&#xf…

【链表OJ—链表的回文结构】

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、pandas是什么&#xff1f;二、使用步骤 1.引入库2.读入数据总结 前言 提示&#xff1a;这里可以添加本文要记录的大概内容&#xff1a; 例如&#xff1a;…

Apache DolphinScheduler 3.1.9 版本发布:提升系统的稳定性和性能

&#x1f680;我们很高兴宣布&#xff0c;Apache DolphinScheduler 的最新版本 3.1.9 已正式发布&#xff01;此版本在 3.1.8 的基础上进行了关键的 bug 修复和文档更新&#xff0c;共计修复了 14 个 bug 和改进了 3 个文档。 主要更新亮点 本次更新重点解决了以下几个关键问题…

《PCI Express体系结构导读》随记 —— 第I篇 第1章 PCI总线的基本知识(19)

接前一篇文章&#xff1a;《PCI Express体系结构导读》随记 —— 第I篇 第1章 PCI总线的基本知识&#xff08;18&#xff09; 1.4 PCI总线的中断机制 1.4.3 中断请求的同步 在PCI总线中&#xff0c;INTx信号是一个异步信号。所谓异步是指INTx信号的传递并不与PCI总线的数据传送…

SpringBoot 医药咨询系统

概述 智慧医药系统&#xff08;smart-medicine&#xff09;是一个基于 SpringBoot 开发的Web 项目。整体页面简约大气&#xff0c;增加了AI医生问诊功能&#xff0c;功能设计的较为简单。 开源地址 https://gitcode.net/NVG_Haru/Java_04 界面预览 功能介绍 游客功能介绍 …

现实世界中的人工智能:工业制造的 4 个成功案例研究

现实世界中的人工智能&#xff1a;工业制造的 4 个成功案例研究 从抓鸡翅到建立整个虚拟工厂&#xff0c;各种规模的制造商都利用人工智能以更快的速度、更低的成本和更低的风险生产更多的产品。 我们能否让工厂变得足够聪明&#xff0c;在发生故障之前告诉我们&#xff1f;我…

C语言实现关键字匹配算法(复制即用)

文章目录 前言功能要求运行截图全部代码 前言 无套路&#xff0c;均已上机通过&#xff0c;求个关注求个赞&#xff0c;提供答疑解惑服务。 功能要求 一份C源代码存储在一个文本文件中&#xff0c;请统计该文件中关键字出现的频度&#xff0c;并按此频度对关键字进行排序。要…

Kafka安装及简单使用介绍

&#x1f353; 简介&#xff1a;java系列技术分享(&#x1f449;持续更新中…&#x1f525;) &#x1f353; 初衷:一起学习、一起进步、坚持不懈 &#x1f353; 如果文章内容有误与您的想法不一致,欢迎大家在评论区指正&#x1f64f; &#x1f353; 希望这篇文章对你有所帮助,欢…

政务大数据能力平台建设方案:文件全文30页,附下载

关键词&#xff1a;智慧政务解决方案&#xff0c;智慧政务建设&#xff0c;智慧政务服务平台&#xff0c;智慧政务大数据&#xff0c;数字政务一体化平台。大数据&#xff0c;政务大数据建设 一、智慧政务建设需求 1、政务服务需求&#xff1a;智慧政务建设需要满足人民群众的…

Jenkins 系列:Jenkins 安装(Windows、Mac、Centos)和简介

文章目录 简介发展历史应用场景 Jenkins 安装部署先决条件硬件要求软件包下载war 包部署linux 系统部署mac 系统部署windows 系统部署安装后基本配置解锁自定义 jenkins 插件创建用户配置更新站点 配置文件 简介 Jenkins前身是 Hudson&#xff0c;使用 java 语言开发的自动化发…

基于SpringBoot实现的前后端分离电影评分项目,功能:注册登录、浏览影片、热门影片、搜索、评分、片单、聊天、动态

一、项目介绍 本项目主要基于SpringBoot、Mybatis-plus、MySQL、Redis实现的影片评分项目。 本系统是前后端分离的&#xff0c;分别由三个子项目构成&#xff1a;java服务端、用户前端、管理员管理前端 关键词&#xff1a;springboot java vue mysql reids websocket 毕业设计…

【Leetcode】1154. 一年中的第几天

文章目录 题目思路代码 题目 1154. 一年中的第几天链接 思路 题目要求是给定一个字符串 date&#xff0c;它代表一个日期&#xff0c;采用标准的 YYYY-MM-DD 格式。需要计算这个日期是当年的第几天。 首先&#xff0c;我们可以通过字符串的索引来提取年、月和日的数值&…

python的pywebio库给孩子做加减法数学题

效果展示 程序执行后&#xff0c;打开浏览器&#xff0c;展示一些100以内的加减法混合运算的数学题并输入答案后判断对错&#xff0c;这样倒是省了买教材的钱了。 在题目下方的框中&#xff0c;输入答案&#xff0c;然后点击提交后&#xff0c; 会输出结果 pywebio库介绍 安装…

Resnet BatchNormalization 迁移学习

时间&#xff1a;2015 网络中的亮点&#xff1a; 超深的网络结构&#xff08;突破1000层&#xff09;提出residual模块使用Batch Normalization加速训练&#xff08;丢弃dropout&#xff09; 层数越深效果越好&#xff1f; 是什么样的原因导致更深的网络导致的训练效果更差呢…

计算机组成原理复习7

内存管理 文章目录 内存管理存储器概述存储器的分类按在计算机中的作用&#xff08;层次&#xff09;分类按存储介质分类按存取方式分类按信息的可保存性分类 存储器的性能指标存储容量单位成本存储速度&#xff1a;数据传输率数据的宽度/存储周期 存储器的层次化结构多级存储系…