天津城乡住房建设厅网站首页/seo关键词排名优化怎样收费
sync.Mutex
- mutex简介
- mutex 方法
- 源码
- 标志位
- 获取锁
- Lock
- lockSlow
- Unlock
- 怎么 调度 goroutine
- runtime 方法
mutex简介
mutex 是 一种实现互斥的同步原语。(go-version 1.21) (还涉及到Go运行时的内部机制)
mutex 方法
- Lock() 方法用于获取锁,如果锁已被其他 goroutine 占用,则调用的 goroutine 会阻塞,直到锁可用。
- Unlock() 方法用于释放锁,调用该方法前必须确保当前 goroutine 持有锁。
- TryLock()方法尝试获取锁,返回是否成功。使用 TryLock 需要谨慎,因为它通常是对互斥锁的误用的迹象。
源码
标志位
mutexLocked:表示锁是否被持有。如果这个标志位被设置,说明锁已经被某个 goroutine 持有。
mutexWoken:表示是否有被唤醒的等待者。如果这个标志位被设置,说明在释放锁的时候有 goroutine 被唤醒。
mutexStarving:表示锁是否处于饥饿模式。在饥饿模式下,锁的所有权会直接从解锁的 goroutine 直接移交给等待队列中的第一个等待者
获取锁
Lock
func (m *Mutex) Lock() {// Fast path: grab unlocked mutex.// 快速路径上, 直接拿到锁了, 一般是第一个协程的时候 , if atomic.CompareAndSwapInt32(&m.state, 0, mutexLocked) {if race.Enabled {race.Acquire(unsafe.Pointer(m))}return}// 获取不到, 就去慢路径上了, 自旋等待、正常模式、饥饿模式 操作// Slow path (outlined so that the fast path can be inlined)m.lockSlow()
}
lockSlow
func (m *Mutex) lockSlow() {// 记录 等待开始的事件var waitStartTime int64// 标记是否是饥饿模式starving := false// 标志是否 已经唤醒awoke := false// 自旋迭代次数iter := 0// 获取当前互斥锁的状态old := m.statefor {// Don't spin in starvation mode, ownership is handed off to waiters// so we won't be able to acquire the mutex anyway.// 检查 当前状态的第0位(mutexLocked 互斥锁)是否为1, 第2位 (mutexStarving 互斥锁饥饿模式) 是否为0, 满足这俩个状态说明当前被锁住,并且不处于饥饿模式// 并且 进行函数检查, runtime_canSpin(1、当前goroutine 是可运行状态, 2、当前goroutine不可抢占),来判断是否有资格进行自旋 if old&(mutexLocked|mutexStarving) == mutexLocked && runtime_canSpin(iter) {// 实现互斥锁的自旋等待机制// Active spinning makes sense.// Try to set mutexWoken flag to inform Unlock// to not wake other blocked goroutines.// !awoke 表示在此之前没有唤醒过其他等待的 Goroutine// old&mutexWoken == 0:表示之前的状态中还没有被唤醒过的标记。// old>>mutexWaiterShift != 0:表示有等待的 Goroutine// atomic.CompareAndSwapInt32(&m.state, old, old|mutexWoken) 尝试唤醒, 原子操作, 将mutexWoken位 置为1 if !awoke && old&mutexWoken == 0 && old>>mutexWaiterShift != 0 &&atomic.CompareAndSwapInt32(&m.state, old, old|mutexWoken) {// 唤醒位 置为true, 唤醒了, 当前goroutine 有争夺锁的机会了awoke = true}// 通过自旋等待的方式竞争锁的所有权。// 在获取锁失败的情况下, 不至于立即阻塞当前 goroutine,而是通过短暂的自旋等待,期望其他 goroutine 尽快释放锁,以便当前 goroutine 有机会获取到锁runtime_doSpin()iter++old = m.statecontinue}new := old// Don't try to acquire starving mutex, new arriving goroutines must queue.//检查当前锁是否处于饥饿模式,如果不是,说明当前 goroutine 是第一个尝试获取锁的,将 mutexLocked 位置为1,表示锁被持有if old&mutexStarving == 0 {new |= mutexLocked}// 检查当前锁是否已经被持有或处于饥饿模式。如果是,表示有其他 goroutine 持有锁或已经处于饥饿模式,将 mutexWaiterShift 位加到 new 中,表示有等待者。if old&(mutexLocked|mutexStarving) != 0 {new += 1 << mutexWaiterShift}// The current goroutine switches mutex to starvation mode.// But if the mutex is currently unlocked, don't do the switch.// Unlock expects that starving mutex has waiters, which will not// be true in this case.// 这段代码用于处理在当前锁状态已经是 starving(饥饿)模式,并且当前锁是被锁住的情况下,将新状态 new 中的 mutexStarving 位设置为1,则将锁切换到饥饿模式if starving && old&mutexLocked != 0 {new |= mutexStarving}// 唤醒标志位if awoke {// The goroutine has been woken from sleep,// so we need to reset the flag in either case.// 在之前没唤醒过了, 但是始终没拿到锁, 所以后面阶段还是需要被重新唤醒, 这个标志位需要被重置if new&mutexWoken == 0 {throw("sync: inconsistent mutex state")}new &^= mutexWoken}//如果成功表示成功获取了锁。if atomic.CompareAndSwapInt32(&m.state, old, new) {// old 状态位是0 , 之前没有锁, 说明成功获取了锁, 结束 退出if old&(mutexLocked|mutexStarving) == 0 {break // locked the mutex with CAS}// else 如果之前的状态已经锁定了, 后续处理逻辑,主要包括处理饥饿模式、等待队列等情况。// If we were already waiting before, queue at the front of the queue.// 当前goroutine在等待队列中排队, 是否使用LIFO方式排队queueLifo := waitStartTime != 0// 等待时间为0 , 刚开始排队, 记录下时间if waitStartTime == 0 {waitStartTime = runtime_nanotime()}// 等待获取锁, 其中 queueLifo 决定了是否使用 LIFO 排队,最后的参数 1 表示等待一个锁。// 1、 当锁处于饥饿模式时,等待锁的 goroutine 将不再与新的 goroutine 竞争锁。而是直接将锁的所有权直接移交给队列中的第一个等待的 goroutine(等待队列的队首) runtime_SemacquireMutex(&m.sema, true, 1) LIFO// 2、 正常模式 runtime_SemacquireMutex(&m.sema, false, 1)FIFO// 通过runtime 包中, 等待队列runtime_SemacquireMutex(&m.sema, queueLifo, 1)// 根据等待时间 判断是否处于饥饿模式starving = starving || runtime_nanotime()-waitStartTime > starvationThresholdNsold = m.state//如果等待时间超过了饥饿模式的阈值,且当前锁的状态是饥饿模式,就尝试退出饥饿模式,切换回正常模式。这里需要考虑到可能的竞态条件,因此使用 CAS(Compare-And-Swap)操作if old&mutexStarving != 0 {// If this goroutine was woken and mutex is in starvation mode,// ownership was handed off to us but mutex is in somewhat// inconsistent state: mutexLocked is not set and we are still// accounted as waiter. Fix that.if old&(mutexLocked|mutexWoken) != 0 || old>>mutexWaiterShift == 0 {throw("sync: inconsistent mutex state")}// 计算状态变化值delta := int32(mutexLocked - 1<<mutexWaiterShift)// 如果不处于饥饿模式,或者当前 Goroutine 是队列中的第一个等待者,表示需要退出饥饿模式。if !starving || old>>mutexWaiterShift == 1 {// Exit starvation mode.// Critical to do it here and consider wait time.// Starvation mode is so inefficient, that two goroutines// can go lock-step infinitely once they switch mutex// to starvation mode.delta -= mutexStarving}// 使用原子操作将互斥锁的状态更新为新值。atomic.AddInt32(&m.state, delta)break}awoke = trueiter = 0} else {// 获取不到 old = m.state}}if race.Enabled {race.Acquire(unsafe.Pointer(m))}
}
Unlock
func (m *Mutex) Unlock() {if race.Enabled {_ = m.staterace.Release(unsafe.Pointer(m))}// Fast path: drop lock bit.// 快速路径:尝试直接获取未锁定的互斥锁new := atomic.AddInt32(&m.state, -mutexLocked)if new != 0 {// Outlined slow path to allow inlining the fast path.// To hide unlockSlow during tracing we skip one extra frame when tracing GoUnblock.m.unlockSlow(new)}
}func (m *Mutex) TryLock() bool {old := m.stateif old&(mutexLocked|mutexStarving) != 0 {return false}// There may be a goroutine waiting for the mutex, but we are// running now and can try to grab the mutex before that// goroutine wakes up.if !atomic.CompareAndSwapInt32(&m.state, old, old|mutexLocked) {return false}if race.Enabled {race.Acquire(unsafe.Pointer(m))}return true
}func (m *Mutex) unlockSlow(new int32) {// 不能解锁 没有锁住的锁if (new+mutexLocked)&mutexLocked == 0 {fatal("sync: unlock of unlocked mutex")}if new&mutexStarving == 0 {// 正常模式old := newfor {// 如果没有等待者或者 goroutine 已经被唤醒或者获取了锁,就不需要唤醒任何人。if old>>mutexWaiterShift == 0 || old&(mutexLocked|mutexWoken|mutexStarving) != 0 {return}// Grab the right to wake someone.// 获取唤醒等待者的权利new = (old - 1<<mutexWaiterShift) | mutexWokenif atomic.CompareAndSwapInt32(&m.state, old, new) {// 释放一个信号量,唤醒一个等待者runtime_Semrelease(&m.sema, false, 1)return}old = m.state}} else {// 饥饿模式 , 使用LIFO队列// Starving mode: handoff mutex ownership to the next waiter, and yield// our time slice so that the next waiter can start to run immediately.// Note: mutexLocked is not set, the waiter will set it after wakeup.// But mutex is still considered locked if mutexStarving is set,// so new coming goroutines won't acquire it.runtime_Semrelease(&m.sema, true, 1)}
}
怎么 调度 goroutine
对goroutine进行调度是通过runtime保重的调度器来实现的
Mutex 的状态: Mutex 的状态信息存储在 state 字段中,其中高位表示已经锁住的数量,低位表示其他信息(比如等待者的数量等)。
等待者队列: Go 的调度器管理着等待者的队列,当一个 goroutine 尝试获取锁但锁已经被其他 goroutine 占用时,它会被放入调度器的等待队列。
调度器的作用: 调度器会负责管理所有的 goroutine,包括它们的状态、调度和等待队列。当锁被释放时,调度器会决定哪个等待中的 goroutine 会被唤醒,然后有机会获取锁。
自旋和阻塞: 在尝试获取锁时,如果锁已经被其他 goroutine 占用,当前 goroutine 会通过自旋等待(短暂的忙等待)或者阻塞等待(让出 CPU 资源,等待调度器通知)。
总体来说,Mutex 的实现是基于 state 字段和调度器的协同工作。它通过调度器来管理等待者的队列,实现了一种高效的锁竞争和等待机制
runtime 方法
1、 runtime_canSpin(iter int):
作用:该函数用于检查当前 goroutine 是否可以进行自旋等待,以避免阻塞。
用法:在自旋等待的时候调用,避免过多的自旋。
2、runtime_doSpin():
作用:实现自旋等待的具体逻辑,包括执行一定的无用操作,使得当前 goroutine 让出 CPU 时间,增加其他 goroutine 获取锁的机会。
用法:在自旋等待的时候调用。
3、runtime_nanotime():
作用:获取当前时间(纳秒级别)。
用法:在等待过程中记录等待的起始时间,用于判断是否需要切换到饥饿模式。
4、runtime_SemacquireMutex(sema *uint32, lifo bool, skipframes int):
作用:在等待获取锁时使用,该函数封装了对信号量的获取操作,可以阻塞当前 goroutine。
参数:sema:信号量指针,用于同步等待。lifo:是否使用后进先出(LIFO)方式排队等待。skipframes:用于在跟踪时跳过的帧数,以隐藏 runtime_SemacquireMutex 的调用。
5、runtime_Semrelease(sema *uint32, handoff bool, skipframes int):
作用:在释放锁时使用,该函数封装了对信号量的释放操作,用于唤醒等待者。
参数:sema:信号量指针,用于同步等待。handoff:是否切换到饥饿模式。skipframes:用于在跟踪时跳过的帧数,以隐藏 runtime_Semrelease 的调用。
这些 runtime 包中的方法提供了底层的并发控制机制,支持互斥锁的实现。它们用于在不同的情况下实现自旋等待、唤醒等待者以及记录时间等操作。
相关文章:

go 源码解读 - sync.Mutex
sync.Mutex mutex简介mutex 方法源码标志位获取锁LocklockSlowUnlock怎么 调度 goroutineruntime 方法 mutex简介 mutex 是 一种实现互斥的同步原语。(go-version 1.21) (还涉及到Go运行时的内部机制)mutex 方法 Lock() 方法用于…...

机器学习系列--R语言随机森林进行生存分析(1)
随机森林(Breiman 2001a)(RF)是一种非参数统计方法,需要没有关于响应的协变关系的分布假设。RF是一种强大的、非线性的技术,通过拟合一组树来稳定预测精度模型估计。随机生存森林(RSF࿰…...
<JavaEE> TCP 的通信机制(四) -- 流量控制 和 拥塞控制
目录 TCP的通信机制的核心特性 五、流量控制 1)什么是“流量控制”? 2)如何做到“流量控制”? 3)“流量控制”的作用 六、拥塞控制 1)什么是“拥塞控制”? 2)如何做到“拥塞…...

智慧监控平台/AI智能视频EasyCVR接口调用编辑通道详细步骤
视频监控TSINGSEE青犀视频平台EasyCVR能在复杂的网络环境中,将分散的各类视频资源进行统一汇聚、整合、集中管理,在视频监控播放上,GB28181视频安防监控汇聚平台可支持1、4、9、16个画面窗口播放,可同时播放多路视频流,…...

Go语言实现KV存储系统:前言
文章目录 前言前提条件持久索引并发总结 前言 你好,我是醉墨居士,最近想做一些存储方面的东西玩玩,我第一时间就想到了能不能自己开发一个保存键值对的存储系统 我找了些资料,准备使用Go语言实现一下,想着有想法咱就…...

代码随想录刷题笔记(DAY1)
前言:因为学校的算法考试让我认识了卡哥,为了下学期冲击大厂实习的理想,我加入了卡哥的算法训练营,从今天开始我每天会更新自己的刷题笔记,与大家一起打卡,一起共勉! Day 1 01. 二分查找 &…...

Linux域名IP映射
本地域名IP映射 在Linux系统中,域名映射可以通过编辑/etc/hosts文件来实现。/etc/hosts文件用于将主机名映射到IP地址,从而实现本地域名解析。它通常被用于在没有DNS服务器的情况下,手动指定特定域名和IP地址的映射关系。 格式:…...

postman使用-03发送请求
文章目录 请求1.新建请求2.选择请求方式3.填写请求URL4.填写请求参数get请求参数在params中填写(填完后在url中会自动显示)post请求参数在body中填写,根据接口文档请求头里面的content-type选择body中的数据类型post请求参数为json-选择raw-选…...

【Spring实战】09 MyBatis Generator
文章目录 1. 依赖2. 配置文件3. 生成代码4. 详细介绍 generatorConfig.xml5. 代码详细总结 Spring MyBatis Generator 是 MyBatis 官方提供的一个强大的工具,它能够基于数据库表结构自动生成 MyBatis 持久层的代码,包括实体类、Mapper 接口和 XML 映射文…...

【自然语言处理】【大模型】 ΨPO:一个理解人类偏好学习的统一理论框架
一个理解人类偏好学习的统一理论框架 《A General Theoretical Paradiam to Understand Learning from Human Preferences》 论文地址:https://arxiv.org/pdf/2310.12036.pdf 相关博客 【自然语言处理】【大模型】 ΨPO:一个理解人类偏好学习的统一理论框…...

计算机网络——传输层(五)
前言: 最重要的网络层我们已经学习完了,下面让我们再往上一层,对网络层的上一层传输层进行一个学习与了解,学习网络层的基本概念和网络层中的TCP协议和UDP协议 目录 编辑一、传输层的概述: 1.传输层: …...

python3处理docx并flask显示
前言: 最近有需求处理docx文件,并讲内容显示到页面,对world进行在线的阅读,这样我这里就使用flaskDocument对docx文件进行处理并显示,下面直接上代码: Document处理: 首先下载Document的库文…...

Python:正则表达式速通,码上上手!
1前言 正则表达式(Regular Expression)是一种用来描述字符串模式的表达式。它是一种强大的文本匹配工具,可以用来搜索、替换和提取符合特定模式的文本。 正则表达式由普通字符(例如字母、数字、符号等)和元字符&#…...

centos7安装nginx并安装部署前端
目录: 一、安装nginx第一种方式(外网)第二种方式(内网) 二、配置前端项目三、Nginx相关命令 好久不用再次使用生疏,这次记录一下 一、安装nginx 第一种方式(外网) 1、下载nginx ng…...

Hive实战:统计总分与平均分
文章目录 一、实战概述二、提出任务三、完成任务(一)准备数据文件1、在虚拟机上创建文本文件2、将文本文件上传到HDFS指定目录 (二)实现步骤1、启动Hive Metastore服务2、启动Hive客户端3、创建Hive表,加载HDFS数据文件…...

Linux:不同计算机使用NFS共享资源
一,安装NFS文件系统 NFS即网络文件系统(network file system),它允许网络中的计算机之间通过网络共享资源。目前,NFS只用于在Linux和UNIX主机间共享文件系统。 #使用mount命令可以将远程主机的文件系统 安装到 本地: #将远程主机…...

leetcode贪心算法题总结(一)
此系列分三章来记录leetcode的有关贪心算法题解,题目我都会给出具体实现代码,如果看不懂的可以后台私信我。 本章目录 1.柠檬水找零2.将数组和减半的最少操作次数3.最大数4.摆动序列5.最长递增子序列6.递增的三元子序列7.最长连续递增序列8.买卖股票的最…...

SQL高级:窗口函数
窗口函数,顾名思义,它的操作对象是窗口,即一个小的数据范围,而不是整个结果集。并且它是一个函数,在SQL中使用,所以一定有返回值。 窗口函数是SQL中非常有趣的部分,这一节我们就来学习一下它。 辅助表 方便我们后边的讲解,这里我们要建一张学生成绩表,建表语句如下…...

Excel formulas 使用总结(更新中)
最近在写task assigment的时候学习到的,记录下。 首先它所有需要写赋值formuls都要用 开头 相等赋值 a1 这个就代表这格的数据和a1是一样的。如果希望其他格和它相同的逻辑,可以直接复制该cell或者直接拖动该cell右下角,他会自动进行匹配…...

华为OD机试 - 两个字符串间的最短路径问题(Java JS Python C)
题目描述 给定两个字符串,分别为字符串 A 与字符串 B。 例如 A字符串为 "ABCABBA",B字符串为 "CBABAC" 可以得到下图 m * n 的二维数组,定义原点为(0,0),终点为(m,n),水平与垂直的每一条边距离为1,映射成坐标系如下图。 从原点 (0,0) 到 (0,A) 为水…...

强敌环伺:金融业信息安全威胁分析——钓鱼和恶意软件
门口的敌人:分析对金融服务的攻击 Akamai会定期针对不同行业发布互联网状态报告(SOTI),介绍相关领域最新的安全趋势和见解。最新的第8卷第3期报告主要以金融服务业为主,分析了该行业所面临的威胁和Akamai的见解。我们发…...

1月1日起,贵阳市退役军人可以免费乘坐公交地铁
广大退役军人是党和国家的宝贵财富,是新时代中国特色社会主义现代化建设的重要力量。为切实增强退役军人的幸福感与获得感,贵阳市信捷科技有限公司以“心系老兵情怀,热忱服务人民”为服务宗旨,积极响应贵阳市政府号召,…...

网络隔离后,怎样建立高效安全的数据安全交换通道?
数据安全对企业生存发展有着举足轻重的影响,数据资产的外泄、破坏都会导致企业无可挽回的经济损失和核心竞争力缺失。数据流动才能让其释放价值,想要保护企业核心资产,就要实现数据安全交换。 很多企业为了防止知识产权、商业机密数据泄露&am…...

Python:PyTorch
简介 PyTorch是一个开源的机器学习库,由Facebook的人工智能研究团队(FAIR)开发,用于应用于机器学习和深度学习的Python程序。PyTorch基于Torch,使用Python语言重新编写,使得它更容易使用和扩展。它支持强大…...

CentOS 5/6/7 基于开源项目制作openssh 9.6p1 rpm包—— 筑梦之路
背景介绍 开源项目地址:https://github.com/boypt/openssh-rpms.git 该项目主要支持了centos 5 、6、7版本,针对使用了比较老的操作系统进行openssh安全加固,还是不错的项目,使用简单、一件制作,欢迎大家去支持作者。…...

python的pandas数据分析处理基础学习
pandas学习 一、 pandas基础 1. 什么是pandas? 一个开源的python类库:用于数据分析、数据处理、数据可视化 高性能容易使用的数据结构容易使用的数据分析工具 很方便和其他类库一起使用: numpy:用于数学计算 scikit-learn&a…...

【Qt-容器类】
Qt编程指南 ■ 顺序容器类■ QList■ QVector■ QLinkedList■ QStack■ QQueue ■ 关联容器类■ QSet■ QMap■ QMultiMap■ QHash■ QMultiHash ■ 顺序容器类 ■ QList QList 比较常用的容器类,以数组列表的形式实现,在前、后添加数据非常快。以下为…...

2023-12-27 语音转文字的whisper应用部署
点击 <C 语言编程核心突破> 快速C语言入门 语音转文字的whisper应用部署 前言一、部署whisper二、部署whisper.cpp总结 前言 要解决问题: 需要一款开源的语音转文字应用, 用于视频自动转换字幕. 想到的思路: openai的whisper以及根据这个模型开发的whisper.cppC应用. …...

MAVLINK生成自定义消息
git clone https://github.com/mavlink/mavlink.gitcd mavlinkgit submodule update --init --recursivepython -m mavgenerate出现以下界面 XML填写自定义xml路径,内容可以参考mavlink/message_definitions/v1.0 Out为输出路径 <?xml version"1.0"…...

【MediaPlayerSource】播放器源内部的音视频sender的创建和使用
来看下声网播放中的sender相关组件设计:MediaPlayerSourceDummy 是一个MediaPlayerSourceImpl ,输入音视频帧到 播放器。player_worker_ 线程触发所有操作,由外部传递,与其他组件公用 MediaPlayerSourceDummy(base::IAgoraService* agora_service, utils::worker_type play…...