每日八股文6.2

每日八股-6.2

  • Go
    • 1.GMP调度原理(这部分多去看看golang三关加深理解)
    • 2.GC(同样多去看看golang三关加深理解)
    • 3.闭包
    • 4.go语言函数是一等公民是什么意思
    • 5.sync.Mutex和sync.RWMutex
    • 6.sync.WaitGroup
    • 7.sync.Cond
    • 8.sync.Pool
    • 9.panic和recover
    • 10.goroutine

Go

1.GMP调度原理(这部分多去看看golang三关加深理解)

Go的GMP调度模型是其并发实现的核心。

  • G (Goroutine): 代表一个协程,是Go语言中并发执行的基本单元。它非常轻量,初始栈空间很小,可以创建成千上万个。
  • M (Machine): 代表一个内核线程 (OS Thread),是真正执行代码的实体。M的数量通常不会太多。
  • P (Processor): 代表一个调度处理器,或者说是G和M之间的调度上下文。P的数量默认等于CPU核心数,可以通过GOMAXPROCS环境变量或运行时函数来设置。

调度流程可以概括为:

  1. 每个P会维护一个可运行的G队列 (Local Run Queue, LRQ)。

  2. M需要获取一个P才能执行G。M会从其绑定的P的LRQ中获取G并执行。

  3. 如果P的LRQ为空,M会尝试从全局G队列 (Global Run Queue, GRQ)或其他P的LRQ中窃取 (Work Stealing) G来执行,以实现负载均衡。

  4. 当G执行系统调用或发生阻塞时,它所占用的M会和P解绑(Hand Off),P会去寻找其他空闲的M,或者创建一个新的M来继续执行队列中的其他G。阻塞的G结束后会尝试重新进入某个P的队列等待执行。

  5. 通过这种方式,GMP模型实现了高效的用户态调度,避免了频繁的内核态线程切换,并能充分利用多核CPU资源。

2.GC(同样多去看看golang三关加深理解)

Go语言的垃圾回收 (GC) 主要是为了自动管理内存,减轻开发者的负担,避免内存泄漏。

Go的GC采用的是并发的三色标记清除 (Concurrent Tri-color Mark-and-Sweep) 算法。

核心思想可以概括为:

  1. 根对象 (Roots): 从程序的全局变量、Goroutine栈上的指针等根对象开始追踪。
  2. 三色标记:
  • 白色: 初始时所有对象都是白色,表示尚未被访问。GC结束后,仍为白色的对象将被回收。
  • 灰色: 对象已被访问,但其引用的其他对象尚未完全扫描。灰色对象是待处理的。
  • 黑色: 对象已被访问,并且其引用的所有对象都已被扫描或标记为灰色。黑色对象是存活的。
  1. 标记阶段 (Marking):
  • GC开始时,所有对象标记为白色。
  • 从根对象开始,将其标记为灰色并放入待扫描队列。
  • 不断从队列中取出灰色对象,将其标记为黑色,并将其引用的白色对象标记为灰色放入队列。
  • 这个标记过程是并发的,意味着应用程序的Goroutine可以和GC的标记线程同时运行,从而大大减少STW (Stop-The-World,即cpu完全用来执行垃圾回收,而不执行代码,这样会造成程序的卡顿) 的时间。
  1. 混合写屏障 (Hybrid Write Barrier,融合了插入写屏障和删除写屏障的思想):
  • GC 开始将栈上的可达对象全部扫描并标记为黑色 (之后不再进行第二次重复扫描,无需 STW),

  • GC 期间,任何在栈上创建的新对象,均为黑色。(混合写屏障加入了这两条对栈区的操作,使得不需要STW)

  • 被删除的对象标记为灰色。(删除写屏障)

  • 被添加的对象标记为灰色。(插入写屏障)

补充: 插入写屏障:只应用于堆区,如果一个对象在并发过程中创建了一个新的对象,那么新的对象直接标记为灰色。
删除写屏障:如果一个对象在并发过程中被删除,那么直接标记该删除的对象为灰色。

  1. 清除阶段 (Sweeping): 标记阶段完成后,所有仍然是白色的对象就是不可达的垃圾对象。清除阶段会回收这些白色对象的内存空间。这个阶段也可以部分并发执行。

3.闭包

实现闭包的一个重要前提就是函数是一等公民,它可以向其它类型的参数一样被传递。如果我们有一个函数,并且在这个函数里面又定义了另一个函数,且这个内部函数可以使用外部函数里面定义的变量,这就是闭包。当我们声明一个匿名函数时,他天生就是一个闭包。

4.go语言函数是一等公民是什么意思

函数是一等公民,意味着可以把函数赋值给变量或存储在数据结构中,也可以把函数作为其它函数的参数或者返回值。

5.sync.Mutex和sync.RWMutex

Mutex 也称为互斥锁,互斥锁就是互相排斥的锁,它可以用作保护临界区的共享资源,保证同一时刻只有一个 goroutine 操作临界区中的共享资源。互斥锁 Mutex类型有两个方法,Lock和 Unlock。

type Mutex struct {state int32 // 互斥锁的状态sema  uint32 // 信号量,用于控制互斥锁的状态
}

使用互斥锁的注意事项

  • Mutex 类型变量的零值是一个未锁定状态的互斥锁
  • Mutex 在首次被使用之后就不能再被拷贝(Mutex 是值类型,拷贝会同时拷贝互斥锁的状态)
  • Mutex 在未锁定状态(还未锁定或已被解锁),调用 Unlock方法,将会引发运行时错误
  • Mutex 的锁定状态与特定 goroutine 没有关联,Mutex 被一个 goroutine 锁定, 可以被另外一个 goroutine 解锁。(不建议使用,必须使用时需要格外小心)
  • Mutex 的 Lock方法和 Unlock方法要成对使用,不要忘记将锁定的互斥锁解锁,一般做法是使用 defer

RWMutex 也称为读写互斥锁,读写互斥锁就是读取/写入互相排斥的锁。它可以由任意数量的读取操作的 goroutine 或单个写入操作的 goroutine 持有。读写互斥锁 RWMutex 类型有五个方法,Lock,Unlock,Rlock,RUnlock 和 RLocker。其中,RLocker 返回一个 Locker 接口,该接口通过调用rw.RLock 和 rw.RUnlock 来实现 Lock 和 Unlock 方法。

type RWMutex struct {w           Mutex        // held if there are pending writerswriterSem   uint32       // semaphore for writers to wait for completing readersreaderSem   uint32       // semaphore for readers to wait for completing writersreaderCount atomic.Int32 // number of pending readersreaderWait  atomic.Int32 // number of departing readers
}

使用读写互斥锁的注意事项

  • RWMutex 类型变量的零值是一个未锁定状态的互斥锁
  • RWMutex 在首次被使用之后就不能再被拷贝
  • RWMutex 的读锁或写锁在未锁定状态,解锁操作都会引发 panic
  • RWMutex 的一个写锁 Lock 去锁定临界区的共享资源,如果临界区的共享资源已被(读锁或写锁)锁定,这个写锁操作的 goroutine 将被阻塞直到解锁
  • RWMutex 的读锁不要用于递归调用,比较容易产生死锁
  • RWMutex 的锁定状态与特定的 goroutine 没有关联。一个 goroutine 可以 RLock(Lock),另一个 goroutine 可以 RUnlock(Unlock)
  • 写锁被解锁后,所有因操作锁定读锁而被阻塞的 goroutine 会被唤醒,并都可以成功锁定读锁
  • 读锁被解锁后,在没有被其他读锁锁定的前提下,所有因操作锁定写锁而被阻塞的 goroutine,其中等待时间最长的一个 goroutine 会被唤醒

Mutex 和 RWMutex 的区别

  • RWMutex 将对临界区的共享资源的读写操作做了区分,RWMutex 可以针对读写操作做不同级别的锁保护。
  • RWMutex 读写锁中包含读锁和写锁,它的 Lock和 Unlock 方法用作写锁保护,它的 Rlock和 RUnlock 方法用作读锁保护。
  • RWMutex 读写锁中的读锁和写锁关系如下:
  • 在写锁处于锁定状态时,操作锁定读锁的 goroutine 会被阻塞。
  • 在写锁处于锁定状态时,操作锁定写锁的 goroutine 会被阻塞。
  • 在读锁处于锁定状态时,操作锁定写锁的 goroutine 会被阻塞。
  • 但是,在读锁处于锁定状态时,操作锁定读锁的 goroutine 不会被阻塞。我们可以理解为读锁保护的临界区的共享资源,多个读操作可以同时执行

6.sync.WaitGroup

sync.WaitGroup 用于阻塞等待一组 Go 程的结束。如果有一个任务可以分解成多个子任务进行处理,同时每个子任务没有先后执行顺序的限制,等到全部子任务执行完毕后,再进行下一步处理。这时每个子任务的执行可以并发处理,这种情景下适合使用sync.WaitGroup。

标准用法

  • 启动 Go 程时调用 Add()
  • 在 Go 程结束时调用 Done()
  • 最后调用 Wait()
package main
import ("fmt""sync"
)
func main() {var wg sync.WaitGroupwg.Add(3)go handlerTask1(&wg)go handlerTask2(&wg)go handlerTask3(&wg)wg.Wait()fmt.Println("全部任务执行完毕.")
}
func handlerTask1(wg *sync.WaitGroup) {defer wg.Done()fmt.Println("执行任务 1")
}
func handlerTask2(wg *sync.WaitGroup) {defer wg.Done()fmt.Println("执行任务 2")
}
func handlerTask3(wg *sync.WaitGroup) {defer wg.Done()fmt.Println("执行任务 3")
}

7.sync.Cond

sync.Cond 条件变量用来协调想要访问共享资源的那些 goroutine,当共享资源的状态发生变化的时候,它可以用来通知被互斥锁阻塞的 goroutine。假设一个场景:有一个协程在异步地接收数据,剩下的多个协程必须等待这个协程接收完数据,才能读取到正确的数据。在这种情况下,如果单纯使用 chan 或互斥锁,那么只能有一个协程可以等待,并读取到数据,没办法通知其他的协程也读取数据。这个时候,就需要有个全局的变量来标志第一个协程数据是否接受完毕,剩下的协程,反复检查该变量的值,直到满足要求。或者创建多个 channel,每个协程阻塞在一个 channel 上,由接收数据的协程在数据接收完毕后,逐个通知。总之,需要额外的复杂度来完成这件事。Go 语言在标准库 sync 中内置一个sync.Cond 用来解决这类问题。和sync.Cond相关的有4个方法:

  • NewCond创建实例
  • Broadcast广播唤醒所有
  • Signal唤醒一个协程
  • Wait等待
package main
import ("log""sync""time"
)
var done bool
func main() {// 1. 定义一个互斥锁,用于保护共享数据mu := sync.Mutex{}// 2. 创建一个sync.Cond对象,关联这个互斥锁cond := sync.NewCond(&mu)go read("reader1", cond)go read("reader2", cond)go read("reader3", cond)write("writer", cond)time.Sleep(time.Second * 3)
}
func read(name string, c *sync.Cond) {// 3. 在需要等待条件变量的地方,获取这个互斥锁,并使用Wait方法等待条件变量被通知;c.L.Lock()for !done {c.Wait()}log.Println(name, "starts reading")c.L.Unlock()
}
func write(name string, c *sync.Cond) {// 4. 在需要通知等待的协程时,使用Signal或Broadcast方法通知等待的协程。log.Println(name, "starts writing")time.Sleep(time.Second)c.L.Lock()done = truec.L.Unlock()log.Println(name, "wakes all")c.Broadcast() // 如果不广播, read()方法的 log.Println(name, "starts reading")不会执行
}
/*
输出:2024/10/02 21:27:50 writer starts writing2024/10/02 21:27:51 writer wakes all2024/10/02 21:27:51 reader3 starts reading2024/10/02 21:27:51 reader1 starts reading2024/10/02 21:27:51 reader2 starts reading
*/

8.sync.Pool

sync.pool用于保存和复用临时对象,减少内存分配,降低 GC 压力。使用方式:

  • 声明对象池
  • Get & Put
package main
import ("encoding/json""fmt""sync"
)
type Student struct {Name string `json:"name"`
}
var studentPool = sync.Pool{New: func() interface{} {return new(Student)},
}
func main() {buf := `{"name":"Mike"}`stu := studentPool.Get().(*Student)fmt.Println(*stu) // {}err := json.Unmarshal([]byte(buf), stu)if err != nil {fmt.Printf("err:%s,err")return}fmt.Println(*stu)studentPool.Put(stu) // {Mike}stu2 := studentPool.Get().(*Student)fmt.Println(*stu2) // {Mike}
}

9.panic和recover

在Go语言中,panic和recover构成了处理程序运行时错误的两个基本机制。它们用于在出现严重错误时,能够优雅地终止程序或恢复程序的执行。

  • panic机制
    panic是一个内建函数,用于在程序运行时抛出一个错误。当panic被调用时,当前的函数会立即停止执行,并开始逐层向上"冒泡",直到被recover捕获或到达程序的顶层,导致程序崩溃并输出错误信息。

panic通常用于处理那些无法恢复的错误,比如空指针引用、数组越界等。这些错误如果不加以处理,将会导致程序崩溃。

  • recover机制
    recover是一个内建函数,用于在defer函数中捕获由panic抛出的错误。当panic发生时,程序会立即停止当前函数的执行,并开始逐层向上查找是否有defer语句。如果在defer函数中调用了recover,那么panic会被捕获,程序会恢复正常的执行流程,继续执行defer函数之后的代码。

需要注意的是,recover只有在defer函数中直接调用时才有效。在其他地方调用recover是无效的,它将返回nil并且不会终止panic。

10.goroutine

在Go语言中,goroutine(Go routine)是一种轻量级的执行单元。可以将其理解为一个函数的并发执行,类似于线程,但比线程更轻量级。与传统的线程相比,创建和销毁goroutine的开销非常小。在Go程序中,可以轻松地创建成千上万个goroutine,每个goroutine都能够独立执行,而不需要手动管理线程和锁。这使得在Go语言中实现并发变得非常容易。要创建一个goroutine,只需要在函数调用前加上关键字"go"即可。Go语言的运行时系统(runtime)负责调度和管理goroutine的执行。运行时系统在多个逻辑处理器上调度goroutine,使得它们可以并发执行。以下是goroutine的底层实现原理的一些关键点:

  • 栈的动态增长:每个goroutine有一个固定大小的栈空间,初始大小一般很小(几KB)。当需要更多的栈空间时,运行时系统会动态地扩展 栈的大小。这种栈的动态增长使得goroutine可以有效地处理深度递归或者大型数据结构。
  • 上下文切换:当一个goroutine遇到阻塞操作(如等待I/O、等待通道的数据等)时,运行时系统会自动将该goroutine切换出执行,并让其他可运行的goroutine继续执行。这种上下文切换是协作式的,是在运行时系统控制下完成的,而不是由操作系统的调度器决定。这使得goroutine的切换非常高效。
  • 调度器:Go语言的运行时系统有一个调度器(scheduler),负责在逻辑处理器上调度和管理goroutine的执行。调度器会根据一些策略(如工作窃取)来决定将goroutine分配给哪个逻辑处理器执行。调度器还会处理阻塞的goroutine,并在其可以继续执行时将其重新调度。
  • 垃圾回收:运行时系统中的垃圾回收器(garbage collector)负责自动管理内存的分配和回收。垃圾回收器会追踪和收集不再使用的对象,并回收其占用的内存空间。垃圾回收器与goroutine的协作非常紧密,确保在回收内存时不会影响正在执行的goroutine。
  • 同步和通信:在多个goroutine之间进行同步和通信通常使用通道(channel)。通道提供了一种安全可靠的方式来传递数据和同步操作。通道的使用能够确保在goroutine之间的数据传递和同步操作的正确性和可靠性。

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

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

相关文章

【Unity】相机 Cameras

1 前言 主要介绍官方文档中相机模块的内容。 关于“9动态分辨率”,这部分很多API文档只是提了一下,具体细节还需要自己深入API才行。 2 摄像机介绍 Unity 场景在三维空间中表示游戏对象。由于观察者的屏幕是二维屏幕,Unity 需要捕捉视图并将…

SpringBoot(六)--- AOP、ThreadLocal

目录 前言 一、AOP基础 1.入门程序 2. AOP核心概念 3. 底层原理 二、AOP进阶 1.通知类型 抽取切入点 2. 切入点表达式 2.1 execution 2.2 annoation 2.3 连接点详解 三、ThreadLocal 前言 AOP(面向切面编程),面向切面编程实际就…

【深度学习】 19. 生成模型:Diffusion Models

Diffusion Models Diffusion Models 简介 Diffusion 模型是一类通过逐步添加噪声并再逆向还原的方式进行图像生成的深度生成模型。其基本流程包括: 前向过程(Forward Process):将真实图像逐步加噪,最终变为高斯噪声…

Y1——链式前向星

知识点 模版——链表的前插法 head表示头结点的下标 ver[i]表示结点i 的值 tot存储当前已经用到了哪个 add用于将x插到头结点 int head1; intt ver[N],Next[N]; int ttot-1; void add(int x){ver[tot]x;Next[tot]head;headtot; } 常见的链式前向星三种实现形式&#xff…

如何排查Redis单个Key命中率骤降?

问题现象 Redis整体命中率98%,但监控发现特定Key(如user:1000:profile)的命中率从99%骤降至40%,引发服务延迟上升。 排查步骤 1. 确认现象与定位Key // 通过Redis监控工具获取Key指标 public void monitorKey(String key) {Je…

自定义Shell命令行解释器

目录 1、目标 2、显示命令提示符 2.1 getenv 2.2 getcwd 2.3 putenv 3、获取用户输入的命令 4、解析命令 5、处理内建命令 6、处理外部命令 7、完整代码 7.1 myshell.cpp 7.2 Makefile 1、目标 实现一个Linux的myshell,有以下基本的功能。 显示命令提示…

Laplace 噪声

Laplace 噪声是一种特定概率分布(拉普拉斯分布)产生的随机扰动。它是差分隐私(Differential Privacy, DP)中最核心、最常用的噪声机制之一。它的核心作用是在不泄露个体信息的前提下,允许从包含敏感数据的数据库中提取…

基于空天地一体化网络的通信系统matlab性能分析

目录 1.引言 2.算法仿真效果演示 3.数据集格式或算法参数简介 4.MATLAB核心程序 5.算法涉及理论知识概要 5.1 QPSK调制原理 5.2 空天地一体化网络信道模型 5.3 空天地一体化网络信道特性 6.参考文献 7.完整算法代码文件获得 1.引言 空天地一体化网络是一种将卫星通信…

【Delphi】接收windows文件夹中文件拖拽

本文根据EmailX45的视频文件,进行了优化改进,原文参见:Delphi: Drag and Drop Files from Explorer into TPanel / TMemo - YouTube 在Windows中,如果将选择的文件拖动到Delphi程序的控件上,有很多实现方法&#xff0c…

基于热力学熵增原理的EM-GAN

简介 简介:提出基于热力学熵增原理的EM-GAN,通过生成器熵最大化约束增强输出多样性。引入熵敏感激活函数与特征空间熵计算模块,在MNIST/CelebA等数据集上实现FID分数提升23.6%,有效缓解模式崩溃问题。 论文题目:Entropy-Maximized Generative Adversarial Network (EM-G…

HashMap与ConcurrentHashMap详解:实现原理、源码分析与最佳实践

引言 在Java编程中,集合框架是最常用的工具之一,而HashMap和ConcurrentHashMap则是其中使用频率最高的两个Map实现。它们都用于存储键值对数据,但在实现机制、性能特点和适用场景上有着显著差异。 HashMap作为单线程环境下的首选Map实现&am…

CSS之动画(奔跑的熊、两面反转盒子、3D导航栏、旋转木马)

一、 2D转换 1.1 transform: translate( ) 转换(transform) 是CSS3中具有颠覆性的特征之一,可以实现元素的位移、旋转、缩放等效果 移动:translate 旋转:rotate 缩放:scale 下图为2D转换的坐标系 回忆…

【笔记】在 MSYS2(MINGW64)中安装 python-maturin 的记录

#工作记录 📌 安装背景 操作系统:MSYS2 MINGW64当前时间:2025年6月1日Python 版本:3.12(通过 pacman 安装)目标工具:maturin —— 用于构建和发布 Rust 编写的 Python 包 🛠️ 安装…

基于微信小程序的垃圾分类系统

博主介绍:java高级开发,从事互联网行业六年,熟悉各种主流语言,精通java、python、php、爬虫、web开发,已经做了六年的毕业设计程序开发,开发过上千套毕业设计程序,没有什么华丽的语言&#xff0…

工作日记之权限校验-token的实战案例

背景说明 我们组负责维护的一个系统,前端界面挂载在其他两个系统上,因为历史遗留原因,同时也挂在公网上,没有登陆功能和用户体系,只要输入网址就能访问,虽然这个系统是给公司内部人员使用,但是…

mysql双主模式下基于keepalived的虚拟ip实现高可用模式搭建

数据库安装和升级和双主配置的操作可以参考我的另一篇文章: 数据库安装和升级和双主配置 1、在两台服务器都下载和安装keepalived 下载: yumdownloader --resolve keepalived 下载后得到: [rootlocalhost keepalivedRpm]# ll 总用量 1896 …

展会聚焦丨漫途科技亮相2025西北水务博览会!

2025第三届西北水务数字化发展论坛暨供排水节水灌溉新技术设备博览会在兰州甘肃国际会展中心圆满落幕。本届展会以“科技赋能水资源,数智引领新动能”为主题,活动汇集水务集团、科研院所、技术供应商等全产业链参与者,旨在通过前沿技术展示与…

单调栈(打卡)

本篇基于b站灵茶山艾府。 下面是灵神上课讲解的题目与课后作业,课后作业还有三道实在写不下去了,下次再写。 739. 每日温度 给定一个整数数组 temperatures ,表示每天的温度,返回一个数组 answer ,其中 answer[i] 是…

【机器学习基础】机器学习入门核心算法:层次聚类算法(AGNES算法和 DIANA算法)

机器学习入门核心算法:层次聚类算法(AGNES算法和 DIANA算法) 一、算法逻辑二、算法原理与数学推导1. 距离度量2. 簇间距离计算(连接标准)3. 算法伪代码(凝聚式) 三、模型评估1. 内部评估指标2. …

已有的前端项目打包到tauri运行(windows)

1.打包前端项目产生静态html、css、js 我们接下来用vue3 vite编写一个番茄钟案例来演示。 我们执行npm run build 命令产生的dist目录下的静态文件。 2.创建tarui项目 npm create tauri-applatest一路回车,直到出现。 3.启动运行 我们将打包产生的dist目录下的…