1. 概念梳理
1.1. 进程
进程是计算机中已运行的程序示例,拥有独立的内存空间、资源和系统信息。每个进程都是互相独立的,进程间通信需要通过特定的机制(如管道、消息队列、共享内存、信号、套接字、信号量、内存映射文件、远程过程调用(RPC)等),核心点如下:
(1)独立的内存空间
(2)进程之间相互隔离
(3)创建和销毁开销较大
(4)通常用于操作系统调度的基本单位
1.2. 线程
通常语义中的线程,指的是内核级线程,核心点如下:
(1)是操作系统最小调度单元
(2)创建、销毁、调度交由内核完成,cpu需要完成用户态与内核态的切换
(3)可充分利用多核,实现并行
1.3. 协程
协程,又称为用户级线程,核心点如下:
(1)与线程存在映射关系,为M:1
(2)创建、销毁、调度在用户态完成,对内核透明,所以更轻
(3)从属同一个内核级线程,无法并行;一个协程阻塞会导致从属同一下称的所有协程无法执行
1.4. Goroutine
Goroutine,经Golang优化后的特殊”协程“,核心点如下:
(1)与线程存在映射关系,为M:N;
(2)创建、销毁、调度都在用户态完成,对内核透明,足够轻便
(3)可以利用多线程、实现并行
(4)通过调度器的斡旋,实现和线程间的动态绑定和灵活调度
(5)栈空间大小可动态扩缩,因地制宜
2. GMP模型
2.1. g
g,即goroutine,是golang中对协程的抽象
g有自己的运行栈、生命周期状态、以及执行的任务函数(用户通过go func指定);
g需要绑定在m上执行,在g视角中,可以将m理解为它的cpu
2.2. m
m即machine,是golang中对线程的抽象
m需要和p进行结合,从而进入gmp调度体系之中
m的运行目标始终在g0和g之间切换-当运行g0是执行的是m的调度流程,负责寻找合适的”任务“,也就是g;当运行g时,执行的时m获取到的”任务“,也就是用户通过go func启动的goroutine
2.3. p
p即processor,是golang中的调度器
p可以理解为m的执行代理,m需要与p绑定后,才会进入到gmp调度模式中;因此p的数量决定了g最大并行数量(可由用户通过GOMAXPROCS进行设定,超过CPU核数时无意义)
p是g的存储容器,其自带一个本地g队列(local run queue,简称lrq),承载着一系列等待被调度的g
2.4. gmp
(1)M是线程的抽象;G是goroutine;P是承上启下的调度器
(2)M调度G前,需要和P绑定
(3)全局有多个M和多个P,但同时并行的G的最大数量等于P的数量
(4)G的存放队列有三类:P的本地队列、全局队列和wait队列
(5)M调度G时,优先取P本地队列,其次取全局队列,后取wait队列,这样的好处是,取本地队列时,可以接近于无锁化,减少全局锁竞争
(6)为防止不同P的闲忙差异过大,设立work-stealing机制,本地队列为空的P可以尝试从其他P本地队列偷取一半G补充到自身队列中