Go 基本概念速记速查之协程、并发控制、性能分析
date
Jun 5, 2023
slug
golang-start-goroutine-sync-pprof
status
Published
tags
Go
summary
Go 基本概念汇总速记
type
Post
协程 goroutine
开启协程
go f(x, y, z)
启动一个协程并执行 f 函数,协程们都是运行在相同地址空间内,所以访问共享内容需要做同步控制,sync 包提供了同步原语channel 通道
通道有类型,只能存取指定类型的数据。操作符
<-
,箭头就是数据流动的方向channel 的创建需要用 make
如果没指定 capacity,则通道只能在发送和接收方都准备好时才可通信
双向通道和单向通道
单向通道直接声明出来没有意义,因为一直只能读不能写的通道意味着无法写入也就无法读出,反之也是一样。
单向通道更多的是在特定场景下限制对双向通道的使用约束,例如将一个双向通道作为参数传入某函数中,该函数参数要求接收一个读出通道,意味着在本函数中,通道只能用作读出操作。同样的,另一个函数可以限制通道只能做写入操作。
通道 deadlock 现象
从一个空通道读取时出现 deadlock 问题
下面代码运行会报错
原因是从一个空的通道中读取会发生阻塞,也就是陷入 asleep,但代码中也再无其他协程会写入通道,实际上导致读出操作永远无法被唤醒,go 认为这是发生了死锁,所以抛出异常。
channel 缓冲大小
channel 可以指定缓冲大小,缓冲满了则写入会阻塞,缓冲空则读取会阻塞。
range 遍历通道和 close 关闭通道
close(ch) 发送端可以关闭一个 channel,表示不再产生新数据了,但还可以从中取出数据,如果有的话,且最后一个通道里的数据被接受完之后,再次读取 <-chan 则不会阻塞,而是立即返回 0 和一个值为 false 的标志,表示通道已经关闭。
在接收端可以通过
v, ok := <-ch
的 ok 值得知通道被关闭,如果被关闭,ok 值会是 falsefor i := range c
则可以遍历 channel 直到通道被关闭为止select 多路复用多个 channel
有任何一个可以继续执行,就会执行该分之,当多个分之都准备好时,就随机选一个执行
使用 default 指定默认执行的 select
并发控制
sync.atomic 包
封装了一组原子操作的合集
原子计数
设置和读取
比较并交换
sync.WaitGroup
可以用来等待协程执行完毕
sync.Mutex
互斥锁提供两个方法
Lock
Unlock
SafeCounter 结构体通过 Inc 方法,安全的增加 v Field,在增加v的前后会加上互斥锁和解除互斥锁
sync.RWMutex
读写锁,读锁之间可以并发共享,写锁与其他锁互斥排他。在读多写少的场景下可以提高并发效率。
sync.Cond
条件变量,跟操作系统中的条件变量一样,条件不满足时阻塞并释放锁,条件满足时唤醒并获得锁继续执行。
sync.Map
并发安全的 map 结构,内部实现通过维护两个字典,尽量避开使用锁,提高并发效率。
原生 syncMap 不限制放入的数据类型,例如 string 和 int 都可以存在于同一个 map 中,可以在外包装一层做一下限制
而上面的 IntStrMap 有点过于死板只能处理 int string 类型,想要支持别的类型还需要重新 copy 一遍,下面的 Consurrentmap 通过初始化时指定数据类型来定制一个 map,更灵活一些。
sync.Pool
临时对象池,通过指定一个 New 方法来实现初始化对象操作,当池中没有可用对象时,调用 New 方法获得一个对象
context
协调多协程之间的生命周期,对于处理取消、超时、错误等场景比较方便
性能指标分析
使用 runtime/pprof 统计分析
分析记录 CPU 占用情况
当前文件为 pprof.go,其中多次递归调用 fib 函数。代码中开启 pprof 记录并生成将采样结果保存在 cpu.profile 文件
对生成的 cpu.profile 分析
go tool pprof cpu.profile
进入 pprof 交互模式,使用 top
命令查看最耗时的调用入口能看到在 fib 函数中调用耗时最久。
list 查看函数源码具体调用耗时
分析记录内存消耗情况
流程差不多,模拟一个较为耗内存的操作,大量拼接字符串。
对生成文件进行分析,同样可以 top 和 list 查看
使用 github.com/pkg/profile
简化监控
能看到开启监控的步骤比较固定,
github.com/pkg/profile
这个包就是 runtime/pprof 的一个包装,简化代码流程,一行代码开启监控。下面两个函数分别是监控 CPU 和监控内存的调用方式
更多用法可以查看文档
使用 net/http/pprof
分析 web 服务
在 web 服务主函数中加入一行
之后就可以通过 go tool pprof 远程采样
查看堆内存
go tool pprof http://localhost:6060/debug/pprof/heap
查看 CPU
go tool pprof http://localhost:6060/debug/pprof/profile?seconds=30
采样之后会生成一个压缩文件,会启动浏览器展示查看。
可视化查看指标
使用
go tool pprof -http :8888 xxprofile_采样结果文件
可以 web 形式查看各项指标,更直观一些,能看到火焰图、top 排序、调用链路图等等


参考: