GoLang之Mutex底层系列一(锁、正常模式、饥饿模式)

本文深入探讨Go语言中Mutex的实现,包括正常模式与饥饿模式。在正常模式下,goroutine会尝试自旋获取锁,失败后进入等待队列。饥饿模式下,所有goroutine按FIFO顺序等待,避免尾部延迟。Mutex的状态管理和模式切换确保了高效且公平的锁竞争。
摘要由CSDN通过智能技术生成

GoLang之Mutex底层系列一(锁、正常模式、饥饿模式)

Mutex这个名称的由来,应该是Mutual exclusion的前缀组合,俗称互斥体或者互斥锁。
Go中Mutex的数据结构如下这样的,因为足够简单,所以不需要额外的初始化,此结构的零值就是一个有效的互斥锁,处于Unlocked状态。state存储的是互斥锁的状态,加锁和解锁都是通过atomic包提供的函数原子性来操作该字段。sema用作一个信号量,主要用于等待队列。

在这里插入图片描述
在这里插入图片描述

Mutex有两种模式,在正常模式下,一个尝试加锁的goroutine会先自旋几次,尝试通过原子操作获得锁,

在这里插入图片描述

若几次自旋之后仍不能获得锁,则通过信号量排队等待。
所有等待者会按照先入先出FIFO的顺序排队。

在这里插入图片描述

但是当锁被释放,第一个等待者被唤醒后并不会直接拥有锁,而是需要和后来者竞争,也就是那些处于自旋阶段,尚未排队等待的goroutine。这种情况下后来者更有优势,一方面,它们正在CPU上运行,自然比刚被唤醒的goroutine更有优势,另一方面处于自旋状态的goroutine可以有很多,而被唤醒的goroutine每次只有一个,所以被唤醒的goroutine有很大概率拿不到锁。这种情况下它会被重新插入到队列的头部,而不是尾部。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

而当一个goroutine本次加锁等待时间超过了1ms后,它会把当前Mutex从正常模式切换至“饥饿模式”。
在饥饿模式下,Mutex的所有权从执行Unlock的goroutine,直接传递给等待队列头部的goroutine,后来者不会自旋,也不会尝试获得锁,即使Mutex处于Unlocked的状态。它们会直接到队列的尾部排队等待。

在这里插入图片描述
在这里插入图片描述

当一个等待者获得锁之后,它会在以下两种情况时,将Mutex由饥饿模式切换回正常模式。
第一种情况是它的等待时间小于1ms,也就是它刚来不久

在这里插入图片描述

第二种情况是它是最后一个等待者,等待队列已经空了,后面自然就没有饥饿的goroutine了

在这里插入图片描述

综上所述,在正常模式下自旋和排队是同时存在的,执行lock的goroutine会先一边自旋,尝试几次后如果还没拿到锁,就需要去排队等待了,这种排队之前先让大家来抢的模式,能够有更高的吞吐量,因为频繁的挂起,唤醒goroutine会带来较多的开销。但是又不能无限制的自旋,要把自旋的开销控制在较小的范围内,所以在正常模式下,Mutex有更好的性能。 但是可能会出现队列尾端的goroutine迟迟抢不到锁(尾端延迟)的情况。
而饥饿模式不再尝试自旋,所有goroutine都要排队,严格的FIFO,对于防止出现尾端延迟来讲特别重要。

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

GoGo在努力

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值