做彩票网站模板/资源网站排名优化seo
✍个人博客:Pandaconda-CSDN博客
📣专栏地址:http://t.csdnimg.cn/UWz06📚专栏简介:在这个专栏中,我将会分享 Golang 面试中常见的面试题给大家~
❤️如果有收获的话,欢迎点赞👍收藏📁,您的支持就是我创作的最大动力💪
36. Go Ma p 遍历为什么是无序的?
使用 range 多次遍历 map 时输出的 key 和 value 的顺序可能不同。这是 Go 语言的设计者们有意为之,旨在提示开发者们,Go 底层实现并不保证 map 遍历顺序稳定,请大家不要依赖 range 遍历结果顺序。
主要原因有 2 点:
-
map 在遍历时,并不是从固定的 0 号 bucket 开始遍历的,每次遍历,都会从一个随机值序号的 bucket,再从其中随机的 cell 开始遍历。
-
map 遍历时,是按序遍历 bucket,同时按需遍历 bucket 中和其 overflow bucket 中的 cell。但是 map 在扩容后,会发生 key 的搬迁,这造成原来落在一个 bucket 中的 key,搬迁后,有可能会落到其他 bucket 中了,从这个角度看,遍历 map 的结果就不可能是按照原来的顺序了。
map 本身是无序的,且遍历时顺序还会被随机化,如果想顺序遍历 map,需要对 map key 先排序,再按照 key 的顺序遍历 map。
func TestMapRange(t *testing.T) {m := map[int]string{1: "a", 2: "b", 3: "c"}t.Log("first range:")for i, v := range m {t.Logf("m[%v]=%v ", i, v)}t.Log("second range:")for i, v := range m {t.Logf("m[%v]=%v ", i, v)}// 实现有序遍历var sl []int// 把 key 单独取出放到切片for k := range m {sl = append(sl, k)}// 排序切片sort.Ints(sl)// 以切片中的 key 顺序遍历 map 就是有序的了for _, k := range sl {t.Log(k, m[k])}
}
37. G o Map 为什么是非线程安全的?
map 默认是并发不安全的,同时对 map 进行并发读写时,程序会 panic,原因如下:
Go 官方在经过了长时间的讨论后,认为 Go map 更应适配典型使用场景(不需要从多个 goroutine 中进行安全访问),而不是为了小部分情况(并发访问),导致大部分程序付出加锁代价(性能),决定了不支持。
场景: 2 个协程同时读和写,以下程序会出现致命错误:fatal error: concurrent map writes
package mainimport ("fmt""time"
)func main() {s := make(map[int]int)for i := 0; i < 100; i++ {go func(i int) {s[i] = i}(i)}for i := 0; i < 100; i++ {go func(i int) {fmt.Printf("map第%d个元素值是%d", i, s[i])}(i)}time.Sleep(1 * time.Second)
}
如果想实现 map 线程安全,有两种方式:
1. 使用读写锁 map
+ sync.RWMutex
package mainimport ("fmt""sync""time"
)func main() {var lock sync.RWMutexs := make(map[int]int)for i := 0; i < 100; i++ {go func(i int) {lock.Lock()s[i] = ilock.Unlock()}(i)}for i := 0; i < 100; i++ {go func(i int) {lock.RLock()fmt.Printf("map第%d个元素值是%d
", i, s[i])lock.RUnlock()}(i)}time.Sleep(1 * time.Second)
}
2. 使用 Go 提供的 sync.Map
package mainimport ("fmt""sync""time"
)func main() {var m sync.Mapfor i := 0; i < 100; i++ {go func(i int) {m.Store(i, i)}(i)}for i := 0; i < 100; i++ {go func(i int) {v, ok := m.Load(i)fmt.Printf("Load: %v, %v
", v, ok)}(i)}time.Sleep(1 * time.Second)
}
38. G o map 和 sync.Map 谁的性能好,为什么?
Go 语言的 sync.Map
支持并发读写,采取 “空间换时间” 的机制,冗余了两个数据结构,分别是:read 和 dirty。
type Map struct {mu Mutexread atomic.Value // readOnlydirty map[interface{}]*entrymisses int
}
对比原始 map:
和原始 map + RWLock 的实现并发的方式相比,减少了加锁对性能的影响。它做了一些优化:可以无锁访问 read map,而且会优先操作 read map,倘若只操作 read map 就可以满足要求,那就不用去操作 write map (dirty),所以在某些特定场景中它发生锁竞争的频率会远远小于 map + RWLock 的实现方式。
优点:
适合读多写少的场景。
缺点:
写多的场景,会导致 read map 缓存失效,需要加锁,冲突变多,性能急剧下降。
39. 介绍一下 Channel
在 Go 语言中,Channel(通道)是用于多个 Goroutine 之间进行通信的一种机制,通过它们可以安全地传递数据。
Channel 是一种类型,可以使用内置的 make() 函数来创建它们。创建 Channel 时,需要指定它们可以传输的数据类型。
使用 Channel 时,可以在 Goroutine 之间传递数据,通过它们可以进行同步和异步的操作。在使用 Channel 时,需要注意以下几点:
-
Channel 是引用类型,可以像 Slice 和 Map 一样传递给函数。
-
默认情况下,Channel 是无缓冲的,只有当有 Goroutine 准备好接收数据时,发送操作才会成功。如果发送操作没有被接收,发送的 Goroutine 将会阻塞。
-
通过 make() 函数创建带缓冲的 Channel 时,可以指定缓冲区的大小。在缓冲区没有被填满之前,发送操作不会阻塞。
-
Channel 支持多路复用,可以使用 select 语句在多个 Channel 上进行选择和等待。
-
Channel 可以用于控制 Goroutine 的执行,例如通过关闭 Channel 来通知 Goroutine 退出。
使用 Channel 可以帮助解决并发编程中的一些常见问题,例如避免竞态条件、协调不同 Goroutine 之间的操作等。
40. Go channel 的底层实现原理?
概念:
Go 中的 channel 是一个队列,遵循先进先出的原则,负责协程之间的通信(Go 语言提倡不要通过共享内存来通信,而要通过通信来实现内存共享,CSP (Communicating Sequential Process) 并发模型,就是通过 goroutine 和 channel 来实现的)
使用场景:
-
停止信号监听
-
定时任务
-
生产方和消费方解耦
-
控制并发数
底层数据结构:
通过 var 声明或者 make 函数创建的 channel 变量是一个存储在函数栈帧上的指针,占用 8 个字节,指向堆上的 hchan 结构体。
源码包中 src/runtime/chan.go
定义了 hchan 的数据结构:
hchan 结构体:
type hchan struct {closed uint32 // channel是否关闭的标志elemtype *_type // channel中的元素类型// channel分为无缓冲和有缓冲两种。// 对于有缓冲的channel存储数据,使用了 ring buffer(环形缓冲区) 来缓存写入的数据,本质是循环数组// 为啥是循环数组?普通数组不行吗,普通数组容量固定更适合指定的空间,弹出元素时,普通数组需要全部都前移// 当下标超过数组容量后会回到第一个位置,所以需要有两个字段记录当前读和写的下标位置buf unsafe.Pointer // 指向底层循环数组的指针(环形缓冲区)qcount uint // 循环数组中的元素数量dataqsiz uint // 循环数组的长度elemsize uint16 // 元素的大小sendx uint // 下一次写下标的位置recvx uint // 下一次读下标的位置// 尝试读取channel或向channel写入数据而被阻塞的goroutinerecvq waitq // 读等待队列sendq waitq // 写等待队列lock mutex //互斥锁,保证读写channel时不存在并发竞争问题
}
等待队列:
双向链表,包含一个头结点和一个尾结点。
每个节点是一个 sudog 结构体变量,记录哪个协程在等待,等待的是哪个 channel,等待发送/接收的数据在哪里。
type waitq struct {first *sudoglast *sudog
}
type sudog struct {g *gnext *sudogprev *sudogelem unsafe.Pointerc *hchan...
}
操作:
创建
使用 make(chan T, cap)
来创建 channel,make 语法会在编译时,转换为 makechan64
和 makechan
。
func makechan64(t *chantype, size int64) *hchan {if int64(int(size)) != size {panic(plainError("makechan: size out of range"))}return makechan(t, int(size))
}
创建 channel 有两种,一种是带缓冲的 channel,一种是不带缓冲的 channel。
// 带缓冲
ch := make(chan int, 3)
// 不带缓冲
ch := make(chan int)
创建时会做一些检查:
-
元素大小不能超过 64K。
-
元素的对齐大小不能超过 maxAlign 也就是 8 字节。
-
计算出来的内存是否超过限制。
创建时的策略:
-
如果是无缓冲的 channel,会直接给 hchan 分配内存。
-
如果是有缓冲的 channel,并且元素不包含指针,那么会为 hchan 和底层数组分配一段连续的地址。
-
如果是有缓冲的 channel,并且元素包含指针,那么会为 hchan 和底层数组分别分配地址。
发送
发送操作,编译时转换为 runtime.chansend
函数。
func chansend(c *hchan, ep unsafe.Pointer, block bool, callerpc uintptr) bool
阻塞式:
调用 chansend 函数,并且 block = true。
ch <- 10
非阻塞式:
调用 chansend 函数,并且 block = false。
select {case ch <- 10:...default
}
向 channel 中发送数据时大概分为两大块:检查和数据发送,数据发送流程如下:
-
如果 channel 的读等待队列存在接收者 goroutine
-
将数据直接发送给第一个等待的 goroutine, 唤醒接收的 goroutine。
-
-
如果 channel 的读等待队列不存在接收者 goroutine
-
如果循环数组 buf 未满,那么将会把数据发送到循环数组 buf 的队尾。
-
如果循环数组 buf 已满,这个时候就会走阻塞发送的流程,将当前 goroutine 加入写等待队列,并挂起等待唤醒。
-
接收
发送操作,编译时转换为runtime.chanrecv
函数。
func chanrecv(c *hchan, ep unsafe.Pointer, block bool) (selected, received bool)
阻塞式:
调用 chanrecv 函数,并且 block = true。
<ch
v := <ch
v, ok := <ch
// 当channel关闭时,for循环会自动退出,无需主动监测channel是否关闭,可以防止读取已经关闭的channel,造成读到数据为通道所存储的数据类型的零值
for i := range ch {fmt.Println(i)
}
非阻塞式:
调用 chanrecv 函数,并且 block = false。
select {case <-ch:...default
}
向 channel 中接收数据时大概分为两大块,检查和数据发送,而数据接收流程如下:
-
如果 channel 的写等待队列存在发送者 goroutine
-
如果是无缓冲 channel,直接从第一个发送者 goroutine 那里把数据拷贝给接收变量,唤醒发送的 goroutine。
-
如果是有缓冲 channel(已满),将循环数组 buf 的队首元素拷贝给接收变量,将第一个发送者 goroutine 的数据拷贝到 buf 循环数组队尾,唤醒发送的 goroutine。
-
-
如果 channel 的写等待队列不存在发送者 goroutine
-
如果循环数组 buf 非空,将循环数组 buf 的队首元素拷贝给接收变量。
-
如果循环数组 buf 为空,这个时候就会走阻塞接收的流程,将当前 goroutine 加入读等待队列,并挂起等待唤醒。
-
关闭
关闭操作,调用 close 函数,编译时转换为 runtime.closechan
函数。
close(ch)
func closechan(c *hchan)
案例分析:
package main
import ("fmt""time""unsafe"
)
func main() {// ch是长度为4的带缓冲的channel// 初始hchan结构体重的buf为空,sendx和recvx均为0ch := make(chan string, 4)fmt.Println(ch, unsafe.Sizeof(ch))go sendTask(ch)go receiveTask(ch)time.Sleep(1 * time.Second)
}
// G1是发送者
// 当G1向ch里发送数据时,首先会对buf加锁,然后将task存储的数据copy到buf中,然后sendx++,然后释放对buf的锁
func sendTask(ch chan string) {taskList := []string{"this", "is", "a", "demo"}for _, task := range taskList {ch <- task //发送任务到channel}
}
// G2是接收者
// 当G2消费ch的时候,会首先对buf加锁,然后将buf中的数据copy到task变量对应的内存里,然后recvx++,并释放锁
func receiveTask(ch chan string) {for {task := <-ch //接收任务fmt.Println("received", task) //处理任务}
}
总结 hchan 结构体的主要组成部分有四个:
-
用来保存 goroutine 之间传递数据的循环数组:buf
-
用来记录此循环数组当前发送或接收数据的下标值:sendx 和 recvx
-
用于保存向该 chan 发送和从该 chan 接收数据被阻塞的 goroutine 队列: sendq 和 recvq
-
保证 channel 写入和读取数据时线程安全的锁:lock
相关文章:

【Golang 面试 - 基础题】每日 5 题(八)
✍个人博客:Pandaconda-CSDN博客 📣专栏地址:http://t.csdnimg.cn/UWz06 📚专栏简介:在这个专栏中,我将会分享 Golang 面试中常见的面试题给大家~ ❤️如果有收获的话,欢迎点赞👍收藏…...

LeetCode 算法:在排序数组中查找元素的第一个和最后一个位置 c++
原题链接🔗:在排序数组中查找元素的第一个和最后一个位置 难度:中等⭐️⭐️ 题目 给你一个按照非递减顺序排列的整数数组 nums,和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。 如果数组中不存在目标…...

会话存储、本地存储,路由导航守卫、web会话跟踪、JWT生成token、axios请求拦截、响应拦截
1、会话存储、本地存储 前端浏览器中存储用户信息,会话存储、本地存储、cookie 会话存储(sessionStorage):会话期间存储,关闭浏览器后,数据就会销毁 sessionStorage.setItem("account",resp.d…...

strcmp库函数原型
int strcmp(const char *str1, const char *str2) {unsigned const char *s1 (unsigned const char *) str1;unsigned const char *s2 (unsigned const char *) str2;while (*s1 && *s1 *s2) {s1;s2;}return *s1 - *s2; }while (*s1 && *s1 *s2) 一直循环&…...

在 Vue.js 项目中延迟加载子组件
在 Vue.js 中,当父组件渲染时,子组件的生命周期钩子函数会立即执行,即使这些子组件并未显示。这是因为 Vue.js 会在渲染父组件时实例化所有引用的子组件。为了避免不必要的函数执行,我们可以通过使用 v-if 指令和异步组件延迟加载…...

何时会用到设计模式、七大设计原则介绍
以下关于b站尚硅谷相关设计模式视频的总结 设计模式的重要性: 代码重用性(相同的代码,不用编写很多次)、 可读性(编程规范,便于其他程序员阅读和理解)、 可扩展性(增加新功能时&am…...

编程语言发展历史:赋值与相等运算符的变迁历程
本文摘取自笔者书稿《编程语言发展历史》 赋值运算符是编程语言最基础的运算符,其发展历史也非常有趣。最早的赋值语句就是使用等号“”来表示,一些语言为了让赋值运算在数学形式上更加严谨(形如“x x 1”的表达式在数学上不成立࿰…...

求职Leetcode题目(2)
1.柱状图中最大的矩形 据说这是2024年字节二面的题目,我感觉这道题跟接雨水有点类似,最重要的思路还是要找到什么时候能形成矩形的这么个情况,某个范围的矩形的高度,是由最短的柱形来决定的。 我们先整理一下,解决这道…...

深入探索 Postman:使用 API 性能测试优化你的 Web 服务
引言 在当今快速发展的互联网时代,Web 服务的性能至关重要。API 作为服务之间的桥梁,其性能直接影响到整个应用的响应速度和用户体验。Postman,作为一个多功能的 API 开发工具,提供了强大的性能测试功能,帮助开发者评…...

校车购票小程序的设计
管理员账户功能包括:系统首页,个人中心,学生管理,我的乘车信息管理,车辆信息管理,座位管理,系统管理 微信端账号功能包括:系统首页,车辆信息,我的 开发系统…...

拯救数据危机!2024年最受欢迎的数据恢复软件评测
现在大家快速传输资料的方式都变成了电子档,有些数据是存储在电脑上,有些存储在手机,有的存储在U盘甚至其他一些电子设备上。电子设备存储数据方便,丢失数据也总在意料之外。很多时候我们多学会一个工具,比如转转大师数…...

记一次因为在html两个地方引入vue.js导致组件注入失败的问题
这个问题我遇到两次了,是在恼火,不对,三次了,我如果不做这个笔记,我确定我还会遇到第三次。 尾部这个去掉就行 因为头部有了 遇到这种bu g好恼火,解决了又怎么样呢?重蹈覆辙的滋味不好受...

Postman中的智慧重试:API测试用例的错误处理与重试逻辑设置
Postman中的智慧重试:API测试用例的错误处理与重试逻辑设置 在API测试过程中,错误处理和重试逻辑是确保测试准确性和可靠性的重要环节。Postman提供了多种功能来处理测试中可能出现的错误,并允许自定义重试逻辑以适应不同的测试场景。本文将…...

docker部署本地词向量模型
开源项目:GitHub - huggingface/text-embeddings-inference: A blazing fast inference solution for text embeddings models 1. 下载词向量模型 参考我的另一篇博客:langchain 加载本地词向量模型 2. 部署词向量模型 就三行命令 model/data/BAAI/…...

接口自动化中对于文件上传的处理方法
正常的接口自动化基本都是json的格式,对于文件上传是一种特殊的格式是表单格式针对这种表单格式在接口自动化中怎么处理,主要通过工作中使用的一个实际的例子进行分享 举例:web上需要导入一个文件实现相关的功能,主要通过两个接口…...

Java高频面试题分享
文章目录 1. 策略模式怎么控制策略的选取1.1 追问:如果有100种策略呢?1.2 追问:什么情况下初始化Map 2. 什么是索引?什么时候用索引?2.1 追问:怎么判断系统什么时候用量比较少2.2 追问:如何实时…...

kvm虚拟化平台部署
kvm虚拟化平台部署 kvm概念简介 kvm自linux2.6版本以后就整合到内核中,因此可以看做是一个原生架构. kvm虚拟化架构 硬件底层提供物理层面的硬件支持 linux(host),就相当于这个架构中的宿主机,上面运行了多个虚拟机。…...

利用arthas热更新class文件
利用arthas热更新class文件 背景:发现一个bug,家里难以复现,需要在现场环境更新几行代码验证。 arthas-boot version: 3.7.1 java -jar arthas-boot.jar启动arthas 1、利用arthas的sc命令查找确定类名称 sc com.**2、反编译为java文件 …...

天机学堂 第四天 高并发优化总结
前端每隔15秒就发起一次请求,将播放记录写入数据库。 但问题是,提交播放记录的业务太复杂了,其中涉及到大量的数据库操作: 如何进行优化 单机并发能力 变同步为异步 合并写请求 提高单机并发:优化SQL,尽…...

Canva收购Leonardo.ai,增强生成式AI技术能力
每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗?订阅我们的简报,深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同,从行业内部的深度分析和实用指南中受益。不要错过这个机会,成为AI领…...

前端练习<HtmlCSS>——照片墙(附完整代码及实现效果)
这个小练习也来源于b站up小K师兄,大家可以通过下面的链接学习哦~up讲的非常详细。 纯CSS写一个简单酷炫的照片墙效果~ 先看一下这个照片墙的效果: 1.鼠标没有放到图片上时,照片同比例,每张照片都有倒影的效果。 2.然…...

PHP基于微信小程序的打车平台-计算机毕业设计源码78689
摘 要 本文介绍的是基于PHP开发的打车平台小程序。该系统旨在为用户提供一个便捷、高效的平台,以实现网约车的打车功能。随着社交媒体和互联网的普及,网约车已成为日常交通中常见的形式。然而,传统的打车方式存在不方便、不及时等问题。 微信…...

Vue element ui分页组件示例
https://andi.cn/page/621615.html...

redis存储结构
一、整体结构图 二、redisDb结构体 Redis是一个高性能的键值存储系统,它支持多种类型的数据结构,如字符串、列表、集合、散列等。Redis数据库由多个数据库组成,每个数据库用一个redisDb结构体来表示。 dict *dict; dict指向一个字典结构的指…...

SQL Server 数据误删的恢复
在日常的数据库管理中,数据的误删操作是难以避免的。为了确保数据的安全性和完整性,我们必须采取一些措施来进行数据的备份和恢复。本文将详细介绍如何在 SQL Server 中进行数据的备份和恢复操作,特别是在发生数据误删的情况下。假设我们已经…...

墨烯的C语言技术栈-C语言基础-018
char c; //1byte字节 8bit比特位 int main() { int a 10; //向内存申请四个字节,存储10 &a; //取地址操作符 return 0; } 每个字节都有地址 而a的地址就是它第一个字节的地址 要先开始调试才可以查看监控和查看内存 左边是地址 中间是内存中的数据 最后面的是…...

C端与B端 - 第一弹 - 理解和区分C端与B端软件开发
作者:逍遥Sean 简介:一个主修Java的Web网站\游戏服务器后端开发者 主页:https://blog.csdn.net/Ureliable 觉得博主文章不错的话,可以三连支持一下~ 如有疑问和建议,请私信或评论留言! 前言 在软件开发领域…...

穿越多元宇宙的.NET:一场跨平台的星际旅行
概述 在软件开发的浩瀚宇宙中,.NET无疑是一颗耀眼的恒星,散发着多平台开发的光芒。从单一的.NET Framework出发,我们如今已拥有一个多元化的.NET宇宙,每个变体都是一个独特的星球,拥有自己的生态系统和生存法则。本文将…...

Python自学第五天
# 嵌套 字典嵌套字典 # 字典列表 now {pet:cat,color:black} now1 {pet:cat,color:pipe} wq [now,now1] # 这里是中括号 不是花括号 花括号打印不出来 for ff in wq:print(ff) # 创建20个外星人 打印前三个 并且显示一共创建了多少个外星人 now [] for wq in range(20):# 这…...

Cookie-Monster:一款针对Web浏览器的安全分析与数据提取工具
关于Cookie-Monster Cookie-Monster是一款针对常见Web浏览器的安全分析与数据提取工具,该工具可以帮助广大研究人员提取并分析Edge、Chrome和Firefox浏览器中的Cookie数据。 Cookie-Monster适用于红队和蓝队成员,能够提取WebKit主密钥,找到具…...