Go面试题:锁的实现原理sync-mutex篇

2023-09-19 08:22:37

在Go中,主要实现了两种锁:sync.Mutex(互斥锁) 以及 sync.RWMutex(读写锁)。

本篇主要给大家介绍sync.Mutex的使用和实现原理。

为什么需要锁

在高并发下或多goroutine同时执行下,可能会同时读写同一块内存,比如如下场景:

var count int
var mu sync.Mutex

func func1() {
	for i := 0; i < 1000; i++ {
		go func() {
			count = count + 1
		}()
	}
	time.Sleep(time.Second)
	fmt.Println(count)
}

输出的值预期是1000,实际是 948,965等,多次运行结果不一致。
之所以出现这样的现象,是因为对于count=count+1来讲,每个goroutine执行步骤为:

  • 读取当前count值
  • count+1
  • 修改count值

当多个goroutine同时执行修改数值时,后面执行的goroutine会把前面goroutine对count的修改覆盖。

在Go中对于并发程序进行公共资源的访问的限制最常用的就是互斥锁(sync.mutex)的方式

sync.mutex的常用方法有两个:

  • Mutex.lock()用来获取锁
  • Mutex.Unlock()用于释放锁
    在 Lock 和 Unlock 方法之间的代码段称为资源的临界区,这一区间的代码是严格被锁保护的,是线程安全的,任何一个时间点最多只能有一个goroutine在执行。

基于此,上面的示例可以采用sync.mutex来改进:

var count int
var mutex sync.Mutex

func func2() {
	for i := 0; i < 1000; i++ {
		go func() {
			mutex.Lock()
			count = count + 1
			mutex.Unlock()
		}()
	}
	time.Sleep(time.Second)
	fmt.Println(count)
}

输出结果为1000。

当某一goroutine执行了mutex.lock()方法后,如果有其他的goroutine来执行上锁操作,会被阻塞,直到当前的goroutine执行mutex.unlock()方法释放锁后其他的goroutine才会继续抢锁执行。

实现原理

sync.Mutex的数据结构
Go中的sync.Mutex的结构体为:

type Mutex struct {
	state int32
	sema  uint32
}

Sync.Mutex由两个字段构成,state用来表示当前互斥锁处于的状态,sema用于控制锁状态的信号量。相信各位道友读完这两个字段的描述后,好像懂了,又好像没懂。下面我们详细理解下这两个字段到底都作了哪些事。
互斥锁state主要记录了如下四种状态:

waiter_num: 记录了当前等待抢这个锁的goroutine数量
starving: 当前锁是否处于饥饿状态 (后文会详解锁的饥饿状态) 0: 正常状态 1: 饥饿状态
woken: 当前锁是否有goroutine已被唤醒。 0:没有goroutine被唤醒; 1: 有goroutine正在加锁过程
locked: 当前锁是否被goroutine持有。 0: 未被持有 1: 已被持有
sema信号量的作用
当持有锁的gorouine释放锁后,会释放sema信号量,这个信号量会唤醒之前抢锁阻塞的gorouine来获取锁。

锁的两种模式

互斥锁在设计上主要有两种模式: 正常模式和饥饿模式。

之所以引入了饥饿模式,是为了保证goroutine获取互斥锁的公平性。所谓公平性,其实就是多个goroutine在获取锁时,goroutine获取锁的顺序,和请求锁的顺序一致,则为公平。
正常模式下,所有阻塞在等待队列中的goroutine会按顺序进行锁获取,当唤醒一个等待队列中的goroutine时,此goroutine并不会直接获取到锁,而是会和新请求锁的goroutine竞争。 通常新请求锁的goroutine更容易获取锁,这是因为新请求锁的goroutine正在占用cpu片执行,大概率可以直接执行到获取到锁的逻辑。

饥饿模式下, 新请求锁的goroutine不会进行锁获取,而是加入到队列尾部阻塞等待获取锁。
饥饿模式的触发条件

  • 当一个goroutine等待锁的时间超过1ms时,互斥锁会切换到饥饿模式

饥饿模式的取消条件:

  • 当获取到锁的这个goroutine是等待锁队列中的最后一个goroutine,互斥锁会切换到正常模式
  • 当获取到锁的这个goroutine的等待时间在1ms之内,互斥锁会切换到正常模式

注意事项

  1. 在一个goroutine中执行Lock()加锁成功后,不要再重复进行加锁,否则会panic。
  2. 在Lock() 之前 执行Unlock()释放锁 会panic
  3. 对于同一把锁,可以在一个goroutine中执行Lock加锁成功后,可以在另外一个gorouine中执行Unlock释放锁。
更多推荐

三维模型3DTile格式轻量化压缩处理的数据质量提升方法分析

三维模型3DTile格式轻量化压缩处理的数据质量提升方法分析在处理三维模型3DTile格式的轻量化压缩时,如何在减少数据量的同时,保证或提升数据质量是一大挑战。以下为一些提升数据质量的方法分析:改进几何简化算法:在进行几何简化时,除了考虑顶点数量的减少,更要注重误差度量和形状特征。选择具有视觉优化功能的算法,例如基于四

Api接口加密策略

接口安全要求:1.防伪装攻击(案例:在公共网络环境中,第三方有意或恶意的调用我们的接口)2.防篡改攻击(案例:在公共网络环境中,请求头/查询字符串/内容在传输过程被修改)3.防重放攻击(案例:在公共网络环境中,请求被截获,稍后被重放或多次重放)4.防数据信息泄漏(案例:截获用户登录请求,截获到账号、密码等)设计原则:1

java - 散列算法 SHA-256 hash值计算

文章目录前言java-散列算法SHA-256hash值计算1.散列算法是什么?2.散列算法的主要特征是什么?3.计算SHA-256值有没有可能重复4.SHA-256算法实现示例前言如果您觉得有用的话,记得给博主点个赞,评论,收藏一键三连啊,写作不易啊^_^。而且听说点赞的人每天的运气都不会太差,实在白嫖的话,那欢迎常来

MQTT Paho Android 支持SSL/TLS(亲测有效)

MQTTPahoAndroid支持SSL/TLS(亲测有效)登录时支持ssl的交互这是调测登录界面设计代码中对ssl/tls的支持使用MqttAndroidClient配置mqtt客户端请求时,不加密及加密方式连接存在以下几点差异:url及端口差异valuri:String=if(tlsConnection){"ssl

CKA真题分析-2023年度

补充信息#补全#aptinstallbash-completionsource<(kubectlcompletionbash)#kubectlconfigget-contexts#cat~/.kube/config|grepcurrent#kubectlconfigcurrent-contextkubectlconfi

数据中心液冷服务器详情说明

目录前言何为液冷服务器?为什么需要液冷?1.数据中心降低PUE的需求2.政策导向3.芯片热功率已经达到风冷散热极限4.液冷比热远大于空气液冷VS风冷,区别在哪?1.液冷服务器跟风冷服务器的区别2.液冷数据中心跟风冷数据中心的区别液冷技术详情冷板式液冷1.优势2.冷板式整机示意图3.风冷服务器改造冷板式3.1技术难点3.

JavaScript 中的变量声明与赋值

3.10JavaScript中的变量声明与赋值。在计算机编程中,使用名称(或标识符)来表示值是最基本的技术之一。将名称与值绑定为我们提供了一种在程序中引用值并利用它们的方式。当涉及到绑定名称与值时,我们通常称之为将值赋给变量。术语“变量”暗示了新的值可以被赋给它,这意味着与变量关联的值在程序执行过程中可能会改变。如果一

windows ---命令详解1

一、cdD:\k8s>cd/?显示当前目录名或改变当前目录。CHDIR[/D][drive:][path]CHDIR[..]CD[/D][drive:][path]CD[..]..指定要改成父目录。1、键入CDdrive:显示指定驱动器中的当前目录。D:\k8s>cdC:C:\Users\lichf12、不带参数只键入

2023华为杯研究生数学建模竞赛选题建议+初步分析

如下为C君的2023华为杯研究生数学建模竞赛(研赛)选题建议+初步分析2023华为杯研究生数学建模竞赛(研赛)选题建议提示:DSC君认为的难度:C=E<D<F,开放度:C=D=E<F。华为专项的题目(A、B题)暂不进行选题分析,不太建议大多数同学选择,对自己专业技能有很大自信的可以选择华为专项的题目。后续团队会直接更新

scrapy框架--

Scrapy是一个用于爬取数据的Python框架。下面是Scrapy框架的基本操作步骤:安装Scrapy:首先,确保你已经安装好了Python和pip。然后,在命令行中运行以下命令安装Scrapy:pipinstallscrapy创建Scrapy项目:使用Scrapy提供的命令行工具创建一个新的Scrapy项目。在命令

Zabbix

Zabbix前言一、内网离线安装1.下载离线RPM包1.1配置国内镜像源1.2下载zabbix所需rpm包2.内网服务器安装zabbix2.1内网服务器环境准备2.2修改yum源2.3安装2.4配置数据库2.5配置zabbix_server.conf2.6配置php配置文件2.7启动服务3.配置zabbixweb界面3

热文推荐