GMP概念
- G(Goroutine):代表Go协程,是参与调度与执行的最小单位。
存储Goroutine执行栈信息、状态、以及任务函数等。G的数量无限制,理论上只受内存的影响。Goroutines 是并发执行的基本单位,相比于传统的线程,它们更轻量,消耗更少的资源,并由运行时系统调度。
- M(Machine): Go 对操作系统线程的封装,可以看作操作系统内核线程。
负责将 Goroutines 映射到真正的操作系统线程上。在运行时系统中,有一个全局的M列表,每个 M 负责调度 Goroutines。当⼀个 Goroutine 需要执行时,它会被分配给一个M,并在该 M 的线程上运行。M 的数量可以根据系统的负载动态调整。
- P(Processor):处理器,用于执行Goroutines的上下文。
包含了运行goroutine的资源,如果线程想运行Goroutine,必须先获取P,P中还包含了可运行的G队列。
GMP 模型的工作原理:
- 当一个 Goroutine 被创建时,它会被放入一个 P 的本地队列。
- 当 P 的本地队列满了,或者某个 Goroutine 长时间没有被调度执行时,P 会尝试从全局队列中获取 Goroutine。
- 如果全局队列也为空,P 会从其他 P 的本地队列中偷取一些 Goroutines,以保证尽可能多地利用所有的处理器。
- M 的数量决定了同时并发执行的 Goroutine 数目。如果某个 M 阻塞(比如在系统调用中),它的工作会被其他 M 接管。
在Go中,线程是运行goroutine的实体,调度器的功能是把可运行的goroutine分配到工作线程上。Goroutine调度器和OS调度器是通过M结合起来的,每个M都代表了一个内核线程,OS调度器负责把内核线程分配到CPU的核上执行。
GMP模型优点 :
- 复用线程:避免频繁的创建销毁线程
- work stealing策略:当本线程绑定的P本地队列中没有可以运行的G时,会尝试从其他P偷G来运行,而不是销毁本线程
- hand off机制:当本线程因为执行某个G发生系统调阻塞时,会将绑定的P释放,将P转移给其他空闲的M去执行
- 多核并行:GOMAXPROCS设置P的数量,因此最多可有GOMAXPROCS个线程分布在多个cpu上同时执行
- 抢占:在其他协程中要等待一个协程主动让出cpu才会让下一个协程执行,而go中一个goroutine最多占用 cpu 10ms,防止其他goroutine被饿死
- 全局队列:当work stealing策略失效时,会从全局队列中获取G来执行
Goroutine被挂起情况:
调度器重新发起调度更换P执行时,会将Goroutine挂起;
- 在channel堵塞的时候;
- 在垃圾回收的时候;
- sleep休眠;
- 锁等待;
- 抢占;
- IO阻塞