golang GMP模型
一、并发和并行
1、并行
要求CPU有多核计算的能力
在同一时刻需要多个线程在多个CPU上同时执行指令,无论宏观还是微观上都会看到多个线程同时在运行
2、并发
并不要求CPU具备多核计算的能力
每核CPU都是独立的,CPU与CPU之间不需要交互
二、线程
线程是操作系统能够调度的最小单位,分为内核态线程和用户态线程
1)、用户线程
1、用户空间的代码创建,管理,销毁
2、同一进程下创建的多个线程对CPU的竞争是以进程为维度的
3、我们一般情况下说的线程其实是用户线程
4、用户线程无法被系统感知,用户线程所属的进程或者内核线程才能被系统调用
2)、内核线程
1、由操作系统创建,管理,调度
2、线程切换的时候CPU需要切换到内核态
3、能够很好的利用多核CPU 并行计算的优势
三、线程模型,3种协程和线程映射关系
一个用户态线程必须要绑定一个内核态线程,但是CPU不知道有用户态线程的存在,它只知道它运行的是一个内核态线程,内核态线程依然叫“线程(Thread)”,用户线程叫"协程(co-routine)"
1、1:1关系
1个协程绑定1个线程
2、N:1关系
N个协程绑定1个线程,优点是协程在用户态线程即完成切换,不会陷入到内核态,这种切换非常的轻量快速。但是也有很大的缺点,1个进程的所有协程都绑定到1个线程上
3、M:N关系
M个协程绑定1个线程,是N:1和1:1类型的结合
四、go协程
gmp流程:
GO调度器的调度过程,首先创建一个G对象,然后G被保存在P的本地队列或者全局队列(global queue)。这时P会唤醒一个M。P按照它的执行顺序继续执行任务。M寻找一个空闲的P,如果找得到,将G与自己绑定。
然后M执行一个调度循环:调用G对象->执行->清理线程->继续寻找Goroutine
1、概念:
Go中,协程被称为goroutine,非常轻量,一个goroutine只占几KB,并且这几KB就足够goroutine运行完,这就能在有限的内存空间内支持大量goroutine,支持更多的并发,虽然一个goroutine的栈只占几KB,但是可伸缩的,如需更多内容,runtime会自动为goroutine分配
Goroutine特点:
---> 占用内存更小(几KB)
---->调度更灵活(runtime调度)
2、GMP模型
G的生命周期:G从创建、保存、被获取、调度和执行、阻塞、销毁、步骤如下:
步骤1:创建G,关键字go func() 创建G
步骤2:保存G,创建的G优先保存到本地队列P,如果P满了,则会平衡部分P到全局队列中
步骤3:唤醒或者新建M执行任务,进入调度循环(步骤4,5,6)
步骤4:M获取G,M首先从P的本地队列获取G,如果P为空,则从全局队列获取G,如果全局队列也为空,则从另一个本地队列偷取一半数量的G(负载均衡),这种从其他P偷取的方式称为work stealing
步骤5:M调度和执行G,M调用G.func()函数执行G
->、如果M在执行G的过程发生系统调用阻塞(同步),会阻塞G和M(操作系统限制),此时P会和当前M解绑,并寻找新的M,如果没有空闲的M就会新建一个M,接管正在阻塞G所属的P,接着继续执行P中其余的G,这种阻塞后释放P的方式成为hand off。当系统调用结束后,这个G会尝试获取一个空闲的P执行,优先获取之前绑定的P,并放入到这个P的本地队列,如果获取不到P,那么这个线程M变成休眠状态,加入到空闲线程中,然后这个G会被放入到全局队列
步骤6:M执行完G后清理线程,重新进入调度循环
G:goroutine,用户级线程
P:不是物理上的CPU。当一个P有任务,需要创建或者唤醒一个系统线程去处理它队列中的任务。P决定同时执行的任务的数量。由GOMAXPROCS配置
p有两种类型的队列:
-
本地队列:同全局队列类似,存放的也是等待运行的G,存的数量有限,不超过256个。新建G时,G优先加到P的本地队列,如果队列满了,则会把本地队列中一半的G移动到全局队列
-
全局队列:存放等待运行的G,是用来平衡不同的P的任务数量,所有的M共享P的全局队列。
M:machine,一个M对应一个内核级线程,相当于内核级线程在go中的映射
3、抢占模型:
work stealing:当线程M无可运行的G时,尝试从其他M绑定的P偷取G,减少空转,提高线程利用率
hand off:当本线程M因为G进行的系统调用阻塞时,线程释放绑定的P,把P转移给其他空闲的M执行,也提高了线程利用率
五、P和M的个数问题
1、P的数量
由启动时环境变量¥GOMAXPROCS或者是由runtime的方法GOMAXPROCS()决定。
2、M的数量
---> go语言本身的线程,go程序启动时,会设置M的最大数量,默认10000
---> runtime/debug中的SetMaxThreads函数,设置M的最大数量
---> 一个M阻塞了,会创建新的M
M与P的数量没有绝对关系,一个M阻塞,P就会去创建或者切换另一个M,所以,即使P的默认数量是1,也会创建很大M出来。
六、P和M何时创建
1、P何时创建:在确定了P的最大数量n后,运行时系统会根据这个数量创建n个P
2、M合适创建:没有足够的M来关联P并运行其中的可运行的G,比如所有的M此时都阻塞住了,而P中还有很多就绪任务,就会去寻找空闲的M,而没有空闲的,就会去创建新的M