当前位置: 首页 > news >正文

【Golang 面试 - 进阶题】每日 3 题(二)

✍个人博客:Pandaconda-CSDN博客

📣专栏地址:http://t.csdnimg.cn/UWz06

📚专栏简介:在这个专栏中,我将会分享 Golang 面试中常见的面试题给大家~
❤️如果有收获的话,欢迎点赞👍收藏📁,您的支持就是我创作的最大动力

4. R WMutex 实现

sync.RWMutex 是 Go 语言标准库提供的一种读写锁,用于在多个 goroutine 同时访问共享资源时进行保护。与 sync.Mutex 类似,sync.RWMutex 也是通过互斥锁实现的,但是它允许多个 goroutine 同时获取读锁,而只允许一个 goroutine 获取写锁。

下面是一个简单的 sync.RWMutex 的实现示例:

type RWMutex struct {writerSem    chan struct{} // 写者用的信号量readerSem    chan struct{} // 读者用的信号量readerCount  int           // 当前持有读锁的goroutine数量writerCount  int           // 当前持有写锁的goroutine数量readerWait   int           // 正在等待读锁的goroutine数量writerWait   int           // 正在等待写锁的goroutine数量writerLocked bool          // 是否有goroutine持有写锁
}
func NewRWMutex() *RWMutex {return &RWMutex{writerSem: make(chan struct{}, 1),readerSem: make(chan struct{}, 1),}
}
// 获取读锁
func (m *RWMutex) RLock() {// 获取读锁的过程需要加锁m.writerSem <- struct{}{} // 防止写者获取锁m.readerSem <- struct{}{} // 获取读锁的信号量// 更新状态m.readerCount++if m.writerLocked || m.writerWait > 0 {m.readerWait++<-m.readerSem // 等待写者释放锁m.readerWait--}// 释放加锁时获取的信号量<-m.writerSem
}
// 释放读锁
func (m *RWMutex) RUnlock() {// 获取读锁的过程需要加锁m.writerSem <- struct{}{} // 防止写者获取锁// 更新状态m.readerCount--if m.readerCount == 0 && m.writerWait > 0 {<-m.writerSem // 优先唤醒写者}// 释放加锁时获取的信号量<-m.writerSem
}
// 获取写锁
func (m *RWMutex) Lock() {// 获取写锁的过程需要加锁m.writerSem <- struct{}{} // 防止其他写者获取锁m.writerWait++// 等待其他goroutine释放读锁或写锁for m.writerLocked || m.readerCount > 0 {<-m.readerSem}// 更新状态m.writerWait--m.writerLocked = true// 释放加锁时获取的信号量<-m.writerSem
}
// 释放写锁
func (m *RWMutex) Unlock() {// 获取写锁的过程需要加锁m.writerSem <- struct{}{}// 更新状态m.writerLocked = falseif m.writerWait > 0 {<-m.writerSem} else if m.readerWait > 0 {for i := 0; i < m.readerCount; i++ {m.readerSem <- struct{}{} // 优先唤醒读者}}// 释放加锁时获取的信号量<-m.writerSem
}

在这个实现中,sync.RWMutex 包含以下成员:

  • writerSemreaderSem:两个用于同步的信号量通道。写锁会在 writerSem 上等待,读锁会在 readerSem 上等待。

  • readerCountwriterCount:当前持有读锁和写锁的 goroutine 数量。

  • readerWaitwriterWait:正在等待读锁和写锁的 goroutine 数量。

  • writerLocked:标记当前是否有 goroutine 持有写锁。

在读锁和写锁获取和释放的过程中,都需要先获取 writerSem 信号量防止其他写者获取锁。获取读锁时还需要获取 readerSem 信号量,而获取写锁时需要等待其他 goroutine 释放读锁或写锁。

这个实现中有两个重要的细节:

  • 优先唤醒写者:在释放读锁或写锁时,如果有正在等待的写锁 goroutine,应该优先唤醒它们,因为写锁的优先级更高。

  • 读锁的等待问题:在等待读锁的 goroutine 中,如果有其他 goroutine 正在持有写锁或等待写锁,那么这些读锁 goroutine 应该等待写锁 goroutine 释放锁,避免因等待读锁而导致写锁饥饿。

5. R WMutex 注意事项

  • RWMutex 是单写多读锁,该锁可以加多个读锁或者一个写锁。

  • 读锁占用的情况下会阻止写,不会阻止读,多个 Goroutine 可以同时获取读锁。

  • 写锁会阻止其他 Goroutine(无论读和写)进来,整个锁由该 Goroutine 独占。

  • 适用于读多写少的场景。

  • RWMutex 类型变量的零值是一个未锁定状态的互斥锁。

  • RWMutex 在首次被使用之后就不能再被拷贝。

  • RWMutex 的读锁或写锁在未锁定状态,解锁操作都会引发 panic。

  • RWMutex 的一个写锁去锁定临界区的共享资源,如果临界区的共享资源已被(读锁或写锁)锁定,这个写锁操作的 goroutine 将被阻塞直到解锁。

  • RWMutex 的读锁不要用于递归调用,比较容易产生死锁。

  • RWMutex 的锁定状态与特定的 goroutine 没有关联。一个 goroutine 可以 RLock(Lock),另一个 goroutine 可以 RUnlock(Unlock)。

  • 写锁被解锁后,所有因操作锁定读锁而被阻塞的 goroutine 会被唤醒,并都可以成功锁定读锁。

  • 读锁被解锁后,在没有被其他读锁锁定的前提下,所有因操作锁定写锁而被阻塞的 Goroutine,其中等待时间最长的一个 Goroutine 会被唤醒。

 6. Go 读写锁的实现原理?

概念:

读写互斥锁 RWMutex,是对 Mutex 的一个扩展,当一个 goroutine 获得了读锁后,其他 goroutine 可以获取读锁,但不能获取写锁;当一个 goroutine 获得了写锁后,其他 goroutine 既不能获取读锁也不能获取写锁(只能存在一个写者或多个读者,可以同时读)。

使用场景:

多于的情况(既保证线程安全,又保证性能不太差)。

底层实现结构:

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

type RWMutex struct {w           Mutex  // 复用互斥锁writerSem   uint32 // 信号量,用于写等待读readerSem   uint32 // 信号量,用于读等待写readerCount int32  // 当前执行读的 goroutine 数量readerWait  int32  // 被阻塞的准备读的 goroutine 的数量
}

操作:

读锁的加锁与释放

func (rw *RWMutex) RLock() // 加读锁
func (rw *RWMutex) RUnlock() // 释放读锁

加读锁

func (rw *RWMutex) RLock() {
// 为什么readerCount会小于0呢?往下看发现writer的Lock()会对readerCount做减法操作(原子操作)if atomic.AddInt32(&rw.readerCount, 1) < 0 {// A writer is pending, wait for it.runtime_Semacquire(&rw.readerSem)}
}

atomic.AddInt32(&rw.readerCount, 1) 调用这个原子方法,对当前在读的数量加 1,如果返回负数,那么说明当前有其他写锁,这时候就调用 runtime_SemacquireMutex 休眠当前 goroutine 等待被唤醒。

释放读锁

解锁的时候对正在读的操作减 1,如果返回值小于 0 那么说明当前有在写的操作,这个时候调用 rUnlockSlow 进入慢速通道。

func (rw *RWMutex) RUnlock() {if r := atomic.AddInt32(&rw.readerCount, -1); r < 0 {rw.rUnlockSlow(r)}
}

被阻塞的准备读的 goroutine 的数量减 1,readerWait 为 0,就表示当前没有正在准备读的 goroutine 这时候调用 runtime_Semrelease 唤醒写操作。

func (rw *RWMutex) rUnlockSlow(r int32) {// A writer is pending.if atomic.AddInt32(&rw.readerWait, -1) == 0 {// The last reader unblocks the writer.runtime_Semrelease(&rw.writerSem, false, 1)}
}

写锁的加锁与释放。

func (rw *RWMutex) Lock() // 加写锁
func (rw *RWMutex) Unlock() // 释放写锁

加写锁

const rwmutexMaxReaders = 1 << 30
func (rw *RWMutex) Lock() {// First, resolve competition with other writers.rw.w.Lock()// Announce to readers there is a pending writer.r := atomic.AddInt32(&rw.readerCount, -rwmutexMaxReaders) + rwmutexMaxReaders// Wait for active readers.if r != 0 && atomic.AddInt32(&rw.readerWait, r) != 0 {runtime_Semacquire(&rw.writerSem)}
}

首先调用互斥锁的 lock,获取到互斥锁之后,如果计算之后当前仍然有其他 goroutine 持有读锁,那么就调用 runtime_SemacquireMutex 休眠当前的 goroutine 等待所有的读操作完成

这里readerCount 原子性加上一个很大的负数,是防止后面的协程能拿到读锁,阻塞读

释放写锁

func (rw *RWMutex) Unlock() {// Announce to readers there is no active writer.r := atomic.AddInt32(&rw.readerCount, rwmutexMaxReaders)// Unblock blocked readers, if any.for i := 0; i < int(r); i++ {runtime_Semrelease(&rw.readerSem, false)}// Allow other writers to proceed.rw.w.Unlock()
}

解锁的操作,会先调用 atomic.AddInt32(&rw.readerCount, rwmutexMaxReaders) 将恢复之前写入的负数,然后根据当前有多少个读操作在等待,循环唤醒

注意点:

  • 读锁或写锁在 Lock() 之前使用 Unlock() 会导致 panic 异常。

  • 使用 Lock() 加锁后,再次 Lock() 会导致死锁(不支持重入),需 Unlock() 解锁后才能再加锁。

  • 锁定状态与 goroutine 没有关联,一个 goroutine 可以 RLock(Lock),另一个 goroutine 可以 RUnlock(Unlock)。

互斥锁和读写锁的区别:

  • 读写锁区分读者和写者,而互斥锁不区分。

  • 互斥锁同一时间只允许一个线程访问该对象,无论读写;读写锁同一时间内只允许一个写者,但是允许多个读者同时读对象。

相关文章:

【Golang 面试 - 进阶题】每日 3 题(二)

✍个人博客&#xff1a;Pandaconda-CSDN博客 &#x1f4e3;专栏地址&#xff1a;http://t.csdnimg.cn/UWz06 &#x1f4da;专栏简介&#xff1a;在这个专栏中&#xff0c;我将会分享 Golang 面试中常见的面试题给大家~ ❤️如果有收获的话&#xff0c;欢迎点赞&#x1f44d;收藏…...

Java中等题-多数元素2(力扣)【摩尔投票升级版】

给定一个大小为 n 的整数数组&#xff0c;找出其中所有出现超过 ⌊ n/3 ⌋ 次的元素。 示例 1&#xff1a; 输入&#xff1a;nums [3,2,3] 输出&#xff1a;[3] 示例 2&#xff1a; 输入&#xff1a;nums [1] 输出&#xff1a;[1]示例 3&#xff1a; 输入&#xff1a;num…...

100条超牛的DOS命令

目录 1. 文件和目录管理 1.1 列出文件和目录 1.1.1 dir 1.1.2 dir /w 1.2 切换目录 1.2.1 cd 1.2.2 cd .. 1.3 创建和删除目录 1.3.1 md / mkdir 1.3.2 rd / rmdir 1.4 文件操作 1.4.1 del / erase 1.4.2 copy 1.5 文件重命名 1.5.1 ren / rename 1.5.2 move …...

大数据信用报告查询会不会留下查询记录?怎么选择查询平台?

最近有不少网友都在咨询一个问题&#xff0c;那就是大数据信用报告查询会不会留下查询记录&#xff0c;会不会对自己的征信产生影响&#xff0c;下面本文就详细为大家介绍一下&#xff0c;希望对你了解大数据信用有帮助。 首先、大数据信用与人行征信是独立的 很多人只知道人行…...

JS【详解】内存泄漏(含泄漏场景、避免方案、检测方法),垃圾回收 GC (含引用计数、标记清除、标记整理、分代式垃圾回收)

内存泄漏 在执行一个长期运行的应用程序时&#xff0c;应用程序分配的内存没有被释放&#xff0c;导致可用内存逐渐减少&#xff0c;最终可能导致浏览器崩溃或者应用性能严重下降的情况&#xff0c;即 JS 内存泄漏 可能导致内存泄漏的场景 不断创建全局变量未及时清理的闭包&…...

第三期书生大模型实战营之Llamaindex RAG实践

基础任务 任务要求&#xff1a;基于 LlamaIndex 构建自己的 RAG 知识库&#xff0c;寻找一个问题 A 在使用 LlamaIndex 之前InternLM2-Chat-1.8B模型不会回答&#xff0c;借助 LlamaIndex 后 InternLM2-Chat-1.8B 模型具备回答 A 的能力&#xff0c;截图保存。 streamlit界面…...

【从0到1进阶Redis】Jedis 理解事务

笔记内容来自B站博主《遇见狂神说》&#xff1a;Redis视频链接 小伙伴们可以熟悉一下本专栏的 Redis 文章&#xff0c;可以更好地理解 正常操作 package oldfe.study;import com.alibaba.fastjson.JSONObject; import redis.clients.jedis.Jedis; import redis.clients.jedis.T…...

MySQL之Lost connection to MySQL server during query复现测试

测试Lost connection to MySQL server during query复现条件 环境报错信息复现测试方式一方式二 环境 Python: 3.8/3.9 Mysql: 5.x 报错信息 File "/Users/xxx/lib/python3.9/site-packages/sqlalchemy/dialects/mysql/base.py", line 2509, in do_rollbackdbapi_con…...

中国AI大模型场景探索及产业应用调研报告

AI大模型发展态势 定义 AI大模型是指在机器学习和深度学习领域中&#xff0c;采用大规模参数(至少在一亿个以上)的神经网络模型&#xff0c;AI大模型在训练过程中需要使用大量的算力和高质量的数据资源。 产业规模 2023年&#xff0c;中国大模型市场规模为147亿。结合《202…...

Linux--shell脚本语言—/—<1>

一、shell简介 Shell是一种程序设计语言。作为命令语言&#xff0c;它交互式解释和执行用户输入的命令或者自动地解释和执行预先设定好的一连串的命令&#xff1b;作为程序设计语言&#xff0c;它定义了各种变量和参数&#xff0c;并提供了许多在高级语言中才具有的控制结构&am…...

【java框架开发技术点】通过反射机制调用类中的私有或受保护的方法

示例 假设我们有一个类 ExampleClass,其中有一个私有方法 privateMethod: public class ExampleClass {private void privateMethod(String message) {System.out.println("Private method called with message: " + message);} }我们可以使用上述代码来调用这个…...

你知道这些鼎鼎大名的Java底层核心公司吗

在讨论Java虚拟机——JVM的时候&#xff0c;有几个知名的&#xff0c;不得不提到的JVM的产品和公司。 一、Oracle HotSpot&#xff1a;这是由Sun公司开发的虚拟机。它由最初的Classic VM开始&#xff0c;到推出崭露头角的Exact VM的虚拟机&#xff0c;是现代化高性能虚拟机的最…...

C++入门级文章

一、一个用于查询C标准库内函数、操作符等的链接 https://legacy.cplusplus.com/reference/ 声明&#xff1a;该文档并非官方文档&#xff0c;但其具有易于查询和使用的优势&#xff0c;足够日常使用。 二、C的第一个程序 1、C语言中的语法在C中仍旧适用&#xff0c;首先我们来…...

modelsim仿真quartus IP

开发环境&#xff1a;quartus prime pro 20&#xff1b;modelsim se-64 10.6d 1. 生成Altera的IP库 使用quartus生成IP库&#xff0c;需要使用Simulation Library Compiler&#xff08;Tools->Launch Simulation Library Compiler&#xff09; 如下图操作&#xff0c;选择…...

PCB设计经验——布线原则

1.连线精简——避免直角布线 导线也应看作一种元器件&#xff0c;有自己的电阻&#xff0c;电感&#xff0c;电容 PCB走线在直角转弯的地方&#xff0c;信号前后部分相互影响&#xff0c;导致分布电容增加&#xff0c;对信号上升沿和下降沿有延缓影响。从阻抗的角度来说&#…...

C++进阶:设计模式___适配器模式

前言 在C的基础语法的学习后,更进一步为应用场景多写代码.其中设计模式是有较大应用空间. 引入 原本在写容器中适配器类有关的帖子,发现适配模式需要先了解,于是试着先写篇和适配器模式相关的帖子 理解什么是适配器类,需要知道什么是适配器模式.适配器模式是设计模式的一种.笔…...

“八股文“在现代编程面试中的角色重塑:助力、阻力还是桥梁?

&#x1f308;所属专栏&#xff1a;【其它】✨作者主页&#xff1a; Mr.Zwq✔️个人简介&#xff1a;一个正在努力学技术的Python领域创作者&#xff0c;擅长爬虫&#xff0c;逆向&#xff0c;全栈方向&#xff0c;专注基础和实战分享&#xff0c;欢迎咨询&#xff01; 您的点…...

Android 安装应用-浏览阶段

应用安装的浏览阶段主要是由PackageManagerService类中的scanPackageNewLI()实现的&#xff0c;看一下它的代码&#xff1a; // TODO: scanPackageNewLI() and scanPackageOnly() should be merged. But, first, commiting// the results / removing app data needs to be move…...

JavaEE 初阶(10)——多线程8之“单例模式”

目录 一. 设计模式 二. 单例模式 2.1 饿汉模式 2.2 懒汉模式 a. 加锁synchronized b. 双重if判定 c. volatile关键字&#xff08;双重检查锁定&#xff09; 一. 设计模式 设计模式是在软件工程中解决常见问题的经典解决方案。针对一些特定场景给出的一些比较好的解决…...

Javascript常见设计模式

JS设计模式学习【待吸收】-CSDN博客 JavaScript 中的设计模式是用来解决常见问题的最佳实践方案。这些模式有助于创建可重用、易于理解和维护的代码。下面列出了一些常见的 JavaScript 设计模式及其代码示例。 1. 单例模式&#xff08;Singleton&#xff09; 单例模式确保一…...

JavaFX布局-SplitPane

JavaFX布局-SplitPane 常用属性orientationpaddingdividerPositionsdisable 实现方式Java实现fxml实现 一个拆分至少两个区域的容器支持水平、垂直布局可以拖动区域的大小初始化大小通过比例设置[0,1] 常用属性 orientation 排列方式&#xff0c;Orientation.VERTICAL、Orien…...

2.MySQL库的操作

创建数据库 创建数据库的代码&#xff1a; CREATE DATABASE [IF NOT EXISTS] db_name [create_specification [,create_specification] ...];​create_specification:[DEFAULT] CHARACTER SET charset_name[DEFAULT] COLLATE collation_name 说明&#xff1a; 大写的表示关键…...

如何学习计算机

不要只盯着计算机语言学习&#xff0c;你现在已经学习了C语言和Java&#xff0c;暑假又规划学习Python&#xff0c;最后你掌握的就是计算机语言包而已。 2. 建议你找一门想要深挖的语言&#xff0c;沿着这个方向继续往后学习知识就行。计算机语言是学不完的&#xff0c;而未来就…...

Spring MVC 快速入门指南及实战演示

1、SpringMVC简介 1.1 背景 Servlet属于web层开发技术&#xff0c;技术特点&#xff1a; 1. 每个请求都需要创建一个Servlet进行处理 2. 创建Servlet存在重复操作 3. 代码灵活性低&#xff0c;开发效率低 是否有技术方案可以解决以上问题&#xff1f; 1.2 SpringMVC概述 Sp…...

在线测评系统(未完结)

文章目录 注意&#xff01;&#xff01;&#xff01;1、多模块开发&#xff08;后端&#xff09;(1).Maven依赖(2)swagger配置 2、判题机开发&#xff08;1&#xff09;docker 前言&#xff1a;大二刚开始接手了本学院的oj&#xff0c;并管理了一段时间&#xff0c;后来老师给我…...

Python 爬虫项目实战(一):破解网易云 VIP 免费下载付费歌曲

前言 网络爬虫&#xff08;Web Crawler&#xff09;&#xff0c;也称为网页蜘蛛&#xff08;Web Spider&#xff09;或网页机器人&#xff08;Web Bot&#xff09;&#xff0c;是一种按照既定规则自动浏览网络并提取信息的程序。爬虫的主要用途包括数据采集、网络索引、内容抓…...

PTA 6-7 统计某类完全平方数

6-7 统计某类完全平方数&#xff08;20分&#xff09; 本题要求实现一个函数&#xff0c;判断任一给定整数N是否满足条件&#xff1a;它是完全平方数&#xff0c;又至少有两位数字相同&#xff0c;如144、676等。 函数接口定义&#xff1a; int IsTheNumber ( const int N );…...

PyFilesystem2 - Python 操作文件系统

文章目录 一、关于 PyFilesystem2二、安装三、快速使用四、指南为什么要使用 PyFilesystem &#xff1f;打开文件系统树打印关闭目录信息子目录处理文件遍历 WalkingGlobbing移动和复制 五、概念路径系统路径沙盒错误 六、资源信息信息对象命名空间基本命名空间细节命名空间访问…...

Bug小记:关于servlet后端渲染界面时出现的问题小记1P

问题1&#xff1a; 问题描述&#xff1a; int delete(Integer Sno);后端在该方法调用时传入参数 req.getParameter("Sno")报错参数应该为Integer类型问题分析&#xff1a;后端通过请求获取到的前端数据都是字符串类型&#xff0c;需要手动转换参数类型 解决方法&a…...

智慧水务项目(二)django(drf)+angular 18 创建通用model,并对orm常用字段进行说明

一、说明 上一篇文章建立一个最简单的项目&#xff0c;现在我们建立一个公共模型&#xff0c;抽取公共字段&#xff0c;以便于后续模块继承&#xff0c;过程之中会对orm常用字段进行说明&#xff0c;用到的介绍一下 二、创建一个db.py 目录如下图 1、代码 from importlib im…...

丰台建设公司网站/重庆网站seo服务

&#xfeff;&#xfeff;使用C#连接MySQL时&#xff0c;经常会用到命名空间using MySql.Data.MySqlClient; 这说明VS中没有添加引用&#xff0c;解决方法如下&#xff1a; 1&#xff0c;下载MySQL.Data.dll&#xff0c;网上很容易找到 2&#xff0c;将其存放到Windows/Syst…...

毕业设计做网站难吗/百度通用网址

2年前&#xff0c;2018年3月12日微信公众号宣布取消留言功能&#xff0c;新注册的账号一律都没有留言功能了&#xff0c;让很多运营者大呼头疼。3天前&#xff0c;2020年8月18日微信公众号推出内测问题功能&#xff0c;可以在文章中和用户互动&#xff0c;解决了很多运营者没办…...

宁波快速建站模板/百度推广外推联系方式

1 项目说明   随着计算机技术的发展和人工智能的兴起&#xff0c;机器学习在许多领域的应用已经取得了巨大的成功。机器学习的性能表现通常与使用的数据量成正比&#xff0c;因此需要收集大量的数据。在大数据背景下&#xff0c;用户和服务商都面临着隐私泄露问题&#xff0c…...

wordpress 统计浏览量/西安seo和网络推广

曾经也在找看看有没有快捷的方法全选&#xff0c;但是网上很多都是重复&#xff0c;并且错误的&#xff0c;比如&#xff1a; 1,$y,这是什么&#xff1f;要写也要写成:1,$y,但是我告诉你&#xff0c;写成:1,$y是全部复制&#xff0c;不是全选 dG&#xff0c;这个就更加误导人了…...

网站项目签约/网店推广平台

1. 如果加密算法是确定性的&#xff08;相同的明文产生相同的密文&#xff09;&#xff0c;那么它对于选择明文攻击是不安全的 2. 随机化加密&#xff0c;每次对相同的密文加密&#xff0c;会产生不同的结果 AES加密模式 ecb:明文分块计算&#xff0c;块与块之间没有影响&#…...

网页设计就业/企业关键词优化公司

2019独角兽企业重金招聘Python工程师标准>>> 基本思路&#xff1a;通过一个队列保存最近10次的登录记录&#xff0c;下一次登录请求来时&#xff0c;通过与队列中的第一条进行比较&#xff0c;如果在1分钟之内则拒绝请求&#xff0c;否则移除队首元素&#xff0c;将…...