Go 互斥锁的实现原理?

Go sync包提供了两种锁类型:互斥锁sync.Mutex 和 读写互斥锁sync.RWMutex,都属于悲观锁。

概念

Mutex是互斥锁,当一个 goroutine 获得了锁后,其他 goroutine 不能获取锁(只能存在一个写者或读者,不能同时读和写)

使用场景

多个线程同时访问临界区,为保证数据的安全,锁住一些共享资源, 以防止并发访问这些共享数据时可能导致的数据不一致问题。

获取锁的线程可以正常访问临界区,未获取到锁的线程等待锁释放后可以尝试获取锁

底层实现结构

互斥锁对应的是底层结构是sync.Mutex结构体,,位于 src/sync/mutex.go中

type Mutex struct {  state int32  sema  uint32}

state表示锁的状态,有锁定、被唤醒、饥饿模式等,并且是用state的二进制位来标识的,不同模式下会有不同的处理方式

mutex_state

sema表示信号量,mutex阻塞队列的定位是通过这个变量来实现的,从而实现goroutine的阻塞和唤醒

mutex_sema

addr = &sema
func semroot(addr *uint32) *semaRoot {  return &semtable[(uintptr(unsafe.Pointer(addr))>>3)%semTabSize].root  
}
root := semroot(addr)
root.queue(addr, s, lifo)
root.dequeue(addr)var semtable [251]struct {  root semaRoot  ...
}type semaRoot struct {  lock  mutex  treap *sudog // root of balanced tree of unique waiters.  nwait uint32 // Number of waiters. Read w/o the lock.  
}type sudog struct {g *g  next *sudog  prev *sudogelem unsafe.Pointer // 指向sema变量waitlink *sudog // g.waiting list or semaRoot  waittail *sudog // semaRoot...
}

操作

锁的实现一般会依赖于原子操作、信号量,通过atomic 包中的一些原子操作来实现锁的锁定,通过信号量来实现线程的阻塞与唤醒

加锁

通过原子操作cas加锁,如果加锁不成功,根据不同的场景选择自旋重试加锁或者阻塞等待被唤醒后加锁

func (m *Mutex) Lock() {// Fast path: 幸运之路,一下就获取到了锁if atomic.CompareAndSwapInt32(&m.state, 0, mutexLocked) {return}// Slow path:缓慢之路,尝试自旋或阻塞获取锁m.lockSlow()
}

解锁

通过原子操作add解锁,如果仍有goroutine在等待,唤醒等待的goroutine

mutex_unlock

func (m *Mutex) Unlock() {  // Fast path: 幸运之路,解锁new := atomic.AddInt32(&m.state, -mutexLocked)  if new != 0 {  // Slow path:如果有等待的goroutine,唤醒等待的goroutinem.unlockSlow()}  
}

注意点:

  • 在 Lock() 之前使用 Unlock() 会导致 panic 异常
  • 使用 Lock() 加锁后,再次 Lock() 会导致死锁(不支持重入),需Unlock()解锁后才能再加锁
  • 锁定状态与 goroutine 没有关联,一个 goroutine 可以 Lock,另一个 goroutine 可以 Unlock

本文节选于Go合集《Go语言面试题精讲》
GOLANG ROADMAP 一个专注Go语言学习、求职的社区。

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

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

相关文章

现货商品国际挂牌撮合系统功能说明书

现货商品国际挂牌撮合系统功能说明书 一、系统概述 现货商品国际挂牌撮合系统是一个基于互联网技术的电子交易平台,旨在为全球现货商品买卖双方提供高效、透明、公正的撮合服务。该系统通过先进的撮合算法和交易规则,确保交易的快速匹配和成交&#xf…

Netty5 入门HelloWorld

一、客户端代码及关键类说明 /*** netty5的客户端* author -zhengzx-**/ public class ClientSocket {public static void main(String[] args) {//服务类Bootstrap bootstrap new Bootstrap();//workerEventLoopGroup worker new NioEventLoopGroup();try {//设置线程池boo…

基础小白快速入门c语言--

变量: 表面理解:在程序运行期间,可以改变数值的数据, 深层次含义:变量实质上代表了一块儿内存区域,我们可以将变量理解为一块儿内存区域的标识,当我们操作变量时,相当于操作了变量…

leetcode hot100 每日温度

在本题中,我们是通过单调栈来解决的,因为我们采用了栈的数据结构,并且,栈内存储的元素是单调的。 本题我们考虑,将气温数组元素的下标存入栈中,首先初始化要把0放入,0是下标的意思。然后我们拿…

回溯热门问题(算法村第十八关白银挑战)

组合总和 39. 组合总和 - 力扣(LeetCode) 给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这…

buuctf misc做题笔记

喵喵喵 使用stegsolve.jar,按BGR顺序提取出一个png图片,是一个只显示一半的二维码,修改图片高度显示全部二维码,解析出一个百度网盘地址,https://pan.baidu.com/s/1pLT2J4f 下载得到压缩包flag.rar。解压成功&#xf…

【JavaEE进阶】 代理模式

文章目录 🍃前言🎋什么叫代理模式🌴静态代理🎍动态代理🚩JDK动态代理🚩CGLIB动态代理 ⭕总结 🍃前言 前面对Spring AOP的详细使用进行了介绍,这篇博客博主将详细讲解一下Spring AOP…

(面试题)数据结构:链表相交

问题:有两个链表,如何判断是否相交,若相交,找出相交的起始节点 一、介绍 链表相交: 若两个链表相交,则两个链表有共同的节点,那从这个节点之后,后面的节点都会重叠,知道…

怎么把人物从图中抠出?分享几种好用的抠图方法

在日常生活中,我们时常需要将人物从繁杂的背景中优雅地提取出来,无论是为了制作一张精美的证件照,还是为了设计一幅引人注目的海报或宣传画。然而,对于许多非专业人士来说,这仿佛是一场与细节的捉迷藏游戏,…

Laravel Octane 和 Swoole 协程的使用分析二

又仔细研究了下 Octane 源码和 Swoole 的文档,关于前几天 Laravel Octane 和 Swoole 协程的使用分析中的猜想,得到进一步验证: Swoole 的 HTTP Server 启动后会创建一个 master 进程和一个 manager 进程;master 进程又会创建多个…

Python——Windows使用Nuitka2.0打包(保姆级教程)

目录 一、Python虚拟环境搭建 1.1、下载Python 1.2、使用 venv方法(创建虚拟环境) 1.3、进入虚拟环境 1.4、用pip下载项目需要的包(与nuitka) 二、 使用 Nuitka 打包 2.1、打包常用命令(使用nuitka --help可查看所…

2024最新版聚合支付彩虹易支付PHP源码

彩虹易支付是一种便捷的支付解决方案,属于聚合易支付平台的一部分。它提供了即时到账功能,无需签约即可使用。通过这个平台,您可以方便地接入多种支付方式,包括支付宝当面付、QQ钱包、财付通、微信扫码支付和个体商户聚合收款码等…