Go 并发编程学习总结 | 青训营
Go 语言原生支持了协程,它是一种轻量级的线程(subroutine)。协程可以暂停执行,并在需要时恢复执行,从而实现非抢占式的多任务处理,调度语言本身去控制,不必担心过多的内核级线程造成不必要的上下文切换
GO 并发模型 GPM
G(Goroutine)
G代表goroutine,是Go语言中轻量级线程的实现。每个goroutine都包含了自己的栈、程序计数器以及所在的M的信息。Goroutine通过关键字 go
启动,可以看作是一种轻量级的函数调用,它可以在独立的执行路径上并发地执行。Goroutine之间的切换由Go语言的运行时系统自动管理,无需显式地进行线程切换。
P(Processor)
P代表处理器,它是Go语言运行时系统中的一个执行上下文(Execution Context)。P负责调度和执行goroutine,每个P都维护一个goroutine队列,其中存储了要执行的goroutine。P的数量决定了真正的并发度,即有多少个goroutine可以同时执行。在运行时系统启动时,会创建一组P,并将它们与M关联起来。P的数目主要是由runtime中的GOMAXPROCS来决定的
M(Machine)
M是Go语言运行时系统(runtime)中的一种实体,代表了一个内核级别的线程(Operating System Thread)。M负责管理和执行goroutine,它与操作系统的线程(例如操作系统中的线程)相对应。在Go程序启动时,会创建一组M,用于执行goroutine。M会负责与操作系统的线程进行关联,以便实现goroutine的并发执行。
共享内存机制 -- 通道
在Go语言当中提倡以通讯共享内存,而非共享内存以通讯,Go语言当中存在关键字chan 实现在多个goroutine中交互通讯 通道支持三种操作,分别是
发送
ch := make(chan int, 1)
ch <- 1
接收
ch := make(chan int, 1)
ch <- 1
recieve = <-ch
关闭
ch := make(chan int, 1)
ch <- 1
recieve = <-ch
close(ch)
chan 内部的状态也可以分为三种 已关闭的,nil chan ,一般chan
发送 | 阻塞 | panic | 成功 |
接收 | 阻塞 | 对应类型空值 | 阻塞或者成功 |
关闭 | panic | panic | 阻塞或者成功 |
channel 还可搭配 select 实现多路复用的效果, 读取已关闭的chan时可以使用 ,ok 模式判断通道是否关闭 完成一个任务时通过close 通道 从而不发送值,实现任务控制
go func() {
// Do Something
close(ch)
}()
<- ch
并发编程库
官方库下的sync包都是包含对goroutine进行控制的工具
Mutex
锁是在并发编程中无处不在的,用于保护临界区资源,加锁后要进行解锁,go的锁不是可重入的,这点要注意,避免发生死锁。 采用defer 关键字很容易的在锁的声明的同时声明关闭锁,避免遗忘。还有RWMutex 读写锁,并发读而控制并发写入。 还有
Atomic
原子类型,实现修改的原子性,避免在多个goroutine下的LoadStore问题,
WaitGroup/ErrGroup
waitGroup 是一个 waitGroup 对象可以等待一组 goroutinue 结束,但是他对错误传递,goroutinue 出错时不再等待其他 goroutinue(减少资源浪费) 都不能很好的解决,那么 errGroup 可以解决这部分问题
Once
并发下实现单例模式,用于文件加载等情况
Context
Context虽然没在sync包下但是在并发编程中也是非常重要的一部分,现阶段的学习还没深入学习context,后续补上 (≧﹏ ≦)
总结
学习过程中并发也是一个很重要的知识点,目前初步了解了GO的并发模型和对应工具库,对底层的实现还没有太深入的学习,学习上基础是很重要的一部分,继续打基础 ヾ(^▽^*)))