Golang GMP原理(1)
Golang GMP原理(1)
概念梳理
线程
线程一般指内核级线程,核心如下:
- 操作系统的最小调度单元
- 创建 销毁 调度由内核完成,cpu要完成内核态与用户态的转换
- 可充分利用多核,实现并行
协程
协程线程对应
协程,又称为用户级线程,核心点如下:
- 与线程存在映射关系,为M:1
- 创建、销毁、调度在用户态完成,对内核透明,所以更轻
- 从属同一个内核级线程,无法并行;一个协程阻塞会导致从属同一线程的所有协程无法执行。
Goroutine
Goroutine是特殊的协程
- 与线程存在映射关系,为M:N
- 创建、销毁、调度在用户态完成,对内核透明,足够轻便
- 可利用多个线程实现并行
- 通过调度器实现和线程间的动态绑定和调度
- 栈空间可动态扩缩。
对比
模型 | 弱依赖内核 | 可并行 | 可应对阻塞 | 栈可动态扩缩 |
---|---|---|---|---|
线程 | × | √ | √ | × |
协程 | √ | × | × | × |
goroutine | √ | √ | √ | √ |
GMP模型
g
- g是goroutine,是对协程的抽象
- g有自己的运行栈、状态、以及执行的任务函数(用户通过go func指定)
- g需要绑定到p才能执行,在g的视角中,p是它的cpu
p
- p是调度器,联系g与m
- p的数量决定了g最大并行数量,可由用户通过GOMAXPROCS进行设定(超过CPU核数无意义)
m
- m是machine 是go中线程的抽象
- m不直接执行g,而是先和p绑定,由其实现代理
- 借由p的存在,m无需和g绑死,也无需记录g的状态信息,因此g在全生命周期中可实现跨m执行
gmp
- m是线程的抽象 g是goroutine p'是调度器
- m调度g前 需要和p绑定
- 全局有多个m和p 同时并行的g最大数量等于p的数量
- g的存放队列有三类:P的本地队列;全局队列;和wait队列(图中未展示,为io阻塞就绪态goroutine队列)
- m调度g时,优先取p本地队列,其次取全局队列,最后取wait队列;这样的好处是,取本地队列时,可以接近于无锁化,减少全局锁竞争;
- 为防止不同p的闲忙差异过大,设立work-stealing机制,本地队列为空的p可以尝试从其他p本地队列偷取一半的g补充到自身队列.