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

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

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

文章目录

    • 为什么需要锁
    • 在Go中对于并发程序进行公共资源的访问的限制最常用的就是互斥锁(sync.mutex)的方式
    • 实现原理
    • 锁的两种模式
    • 注意事项

为什么需要锁

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

var count int
var mu sync.Mutexfunc 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.Mutexfunc func2() {for i := 0; i < 1000; i++ {go func() {mutex.Lock()count = count + 1mutex.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 int32sema  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释放锁。

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

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

相关文章

git branch -r 远程分支显示不全

找回csdn帐号了&#xff0c;计划陆续开始更新最近的笔记√ 没想到很久没登还有人在看 问题描述 git 查看项目远程分支&#xff1a; git branch -r返回结果很少。但GitHub页面上能看到很多的分支。 尝试git remote update&#xff0c;发现结果仍然如此。 PS C:\Users\dell\g…

AUTOSAR汽车电子嵌入式编程精讲300篇-基于AUTOSAR架构的AT控制系统研究与实现

目录 前言 国内外研究现状 国外研究现状 国内研究现状 2 AUTOSAR规范及开发流程

分析各大常用的JS加密优缺点

Base64编码&#xff1a; 优点&#xff1a; 简单&#xff0c;易于实现。不是真正的加密&#xff0c;只是编码&#xff0c;可以用于数据传输和存储。 缺点&#xff1a; 不提供数据保密性&#xff0c;容易被解码。 示例代码&#xff1a; // 编码 const encodedData btoa(Hello,…

Windows 使用nvm安装多个版本的node.js

在 Windows 上&#xff0c;首先你需要安装 Node Version Manager。请访问 nvm-windows GitHub 页面并下载最新版本的 nvm-setup.zip 文件。解压并运行里面的安装程序。 安装完成后&#xff0c;你可以按照以下步骤使用 Node Version Manager (nvm) 来安装和管理多个版本的 Node…

前后端分离毕设项目之基于springboot+vue的笔记记录分享网站设计与实现(内含源码+文档+部署教程)

博主介绍&#xff1a;✌全网粉丝10W,前互联网大厂软件研发、集结硕博英豪成立工作室。专注于计算机相关专业毕业设计项目实战6年之久&#xff0c;选择我们就是选择放心、选择安心毕业✌ &#x1f345;由于篇幅限制&#xff0c;想要获取完整文章或者源码&#xff0c;或者代做&am…

【陕西理工大学-数学软件实训】数学实验报告(8)(数值微积分与方程数值求解)

目录 一、实验目的 二、实验要求 三、实验内容与结果 四、实验心得 一、实验目的 1. 掌握求数值导数和数值积分的方法。 2. 掌握代数方程数值求解的方法。 3. 掌握常微分方程数值求解的方法。 二、实验要求 1. 根据实验内容&#xff0c;编写相应的MATLAB程序&#xff0c…

ChatGLM DeepSpeed/P-Tuning v2 调参

之前尝试了基于ChatGLM-6B使用LoRA进行参数高效微调,本文给大家分享使用DeepSpeed和P-Tuning v2对ChatGLM-6B进行微调,相关代码放置在GitHub上面:llm-action。 ChatGLM-6B简介 ChatGLM-6B相关的简介请查看之前的文章,这里不再赘述。 P-Tuning v2简介 P-Tuning是一种较新…

构建工具vite/webpack

一、vite 快速开始 全局安装vite npm i -g vite创建vite npm create vite安装依赖 npm i运行项目 npm run dev 二、webpack 1、使用步骤 初始化项目npm init -y安装依赖webpack、webpack-cli在项目中创建src目录&#xff0c;然后编写代码&#xff08;默认主文件index.js&a…

Linux常用工具

文章目录 前言一、Linux编辑器-vim使用1.vim的基本概念2. vim的基本操作3. vim命令集1. 正常模式1. 模式切换和光标移动2. 删除文字及复制3. 其他操作 2. 底行模式 二、Linux编译器-gcc/g使用1. 命令和选项2. 预处理3. 编译4. 汇编(生成机器可识别代码)5. 连接(生成可执行文件或…

Docker 部署 MongoDB 服务

拉取最新版本的 MongoDB 镜像&#xff1a; $ sudo docker pull mongo:latest在本地预先创建好 db 和 configdb 目录, 用于映射 MongoDB 容器内的 /data/db 和 /data/configdb 目录。 使用以下命令来运行 MongoDB 容器: $ sudo docker run -itd --name mongo --privilegedtru…

C#webform Static DataTable 多人同时操作网页数据重复问题

在C# Web Forms中&#xff0c;如果声明一个static变量&#xff0c;它将在整个应用程序域&#xff08;Application Domain&#xff09;中保持持久化状态。每个用户的请求都在同一个应用程序域中处理&#xff0c;因此static变量在不同页面间保持相同的值。 当一个用户发起请求时…

LCP 02.分式化简

​​题目来源&#xff1a; leetcode题目&#xff0c;网址&#xff1a;110. 平衡二叉树 - 力扣&#xff08;LeetCode&#xff09; 解题思路&#xff1a; 模拟分式计算过程即可。 解题代码&#xff1a; class Solution {public int[] fraction(int[] cont) {if(cont.length1){…

JPEG算法及例程

JPEG&#xff08;Joint Photographic Experts Group&#xff09;是一种常见的图像压缩算法&#xff0c;用于减小图像文件的大小。它是一种有损压缩算法&#xff0c;即通过牺牲一定的图像质量来实现压缩。 以下是一个简单的JPEG压缩算法的例程&#xff1a; 将输入图像转换为YUV…

抖音seo矩阵系统源代码分享

技术开发注意事项&#xff1a; 确定业务需求&#xff1a;在开发前&#xff0c;需要明确抖音矩阵系统的业务需求&#xff0c;了解用户的需求和使用习惯&#xff0c;明确系统的功能、性能和安全需求。 选择合适的技术方案&#xff1a;根据系统的需求和复杂度&#xff0c;选择合适…

ibevent 定制——libevent 定制内存分配

libevent 定制内存分配 默认情况下,libevent 使用 C 库的内存管理函数在堆上分配内存。通过提供 malloc、realloc和 free 的替代函数,可以让 libevent 使用其他的内存管理器。希望 libevent 使用一个更高效的分配器时;或者希望 libevent 使用一个工具分配器,以便检查内存泄漏时…

有多条业务线,mysql建多库多表比较好还是一个库多个表比较好呢?

选择使用多库多表还是一个库多个表&#xff0c;取决于你的具体情况和需求。以下是一些考虑因素&#xff1a; 数据隔离&#xff1a;如果每条业务线需要完全独立的数据隔离&#xff0c;例如不同业务线的数据不会相互关联或共享&#xff0c;那么使用多库可以更好地实现数据隔离。 …

JavaWeb 学习笔记 3:Servlet

JavaWeb 学习笔记 3&#xff1a;Servlet 1.简介 Servlet 是 JavaEE 定义的一套 Web 应用开发标准&#xff08;接口&#xff09;&#xff0c;实现了该技术的 Web 服务器软件&#xff08;如 Tomcat&#xff09;上可以运行一个 Servlet 容器&#xff0c;只要我们使用 Servlet 技…

Python 移动文件到指定路径

需求&#xff1a;将指定的文件从指定目录移动到用户指定的目标目录。 shutil 是 Python 标准库中的一个模块&#xff0c;它提供了许多文件和文件集合的高级操作。基本上&#xff0c;它可以帮助我们执行文件操作&#xff0c;例如复制、移动、更名和删除。它旨在与 os 模块一起使…

【测试开发】基础篇 · 专业术语 · 软件测试生命周期 · bug的描述 · bug的级别 · bug的生命周期 · 处理争执

【测试开发】基础篇 文章目录 【测试开发】基础篇1. 软件测试生命周期1.1 软件生命周期1.2 软件测试生命周期 2. 描述bug3. 如何定义bug的级别3.1 为什么要对bug进行级别划分3.2 bug的一些常见级别 4. bug的生命周期5. 产生争执这么怎么办&#xff08;处理人际关系&#xff09;…

ChatGPT:URL编码问题——如何正确进行URL编码以处理特殊字符

ChatGPT&#xff1a;URL编码问题——如何正确进行URL编码以处理特殊字符 报错&#xff1a; URISyntaxException: Malformed escape pair at index 192: http://Center/Question/questionList.html?seaKey%E6%8D%AE%E7%BB%9F%E8%AE%A1%EF%BC%8C%E5%9B%A0%E7%81%AB%E7%81%BE%E6%…