一个协程挂起换入另外一个协程是什么过程?
题目来源:腾讯
答案1:
对于进程、线程,都是有内核进行调度,有CPU时间片的概念,进行抢占式调度。协程,又称微线程,纤程。英文名Coroutine。协程的调用有点类似子程序,如程序A调用了子程序B,子程序B调用了子程序C,当子程序C结束了返回子程序B继续执行之后的逻辑,当子程序B运行结束了返回程序A,直到程序A运行结束。但是和子程序相比,协程有挂起的概念,协程可以挂起跳转执行其他协程,合适的时机再跳转回来。
本质上goroutine就是协程,但是完全运行在用户态,采用了MPG模型:
M:内核级线程
G:代表一个goroutine
P:Processor,处理器,用来管理和执行goroutine的。
G-M-P三者的关系与特点:
- P的个数取决于设置的GOMAXPROCS,go新版本默认使用最大内核数,比如你有8核处理器,那么P的数量就是8
- M的数量和P不一定匹配,可以设置很多M,M和P绑定后才可运行,多余的M处于休眠状态。
- P包含一个LRQ(Local Run Queue)本地运行队列,这里面保存着P需要执行的协程G的队列
- 除了每个P自身保存的G的队列外,调度器还拥有一个全局的G队列GRQ(Global Run
Queue),这个队列存储的是所有未分配的协程G。
假设主机是单核的,那么协程运行图是这样:
- 红色部分表示挂起和休眠,黄色部分表示准备就绪等待运行,绿色部分表示正在运行。
- 主机是单核的所以只有一个处理器P,但是系统初始化了两个线程M0和M1,处理器P优先绑定了M0线程,M1进入休眠状态。
- P的LRQ队列里有G1,G2,G3等待处理。P目前正在处理G0,全局等待队列GRQ里保存着G4,G5,表示这两个协程还未分配给P。
- 如果G0在短时间内处理完,P就会从LRQ中取出G1继续处理。并且将GRQ全局队列中的部分协程加入LRQ中。
- 假设现在G1处理速度很慢,系统就会让M0线程休眠,挂起协程G1,唤醒线程M1进行处理其他的协程。这里M1会将M0未处理的协程取走处理。
- 等到M1协程队列中所有协程处理完再次唤醒M0,或者M1处理某个协程时间较长被挂起,M0也会被唤醒。
如果处理器是多核,就会运行多个P和M。
- M0和M1分别运行在不同的内核中,M0处理G1,G2,G3,M1处理G4,G5,G6。
- 协程挂起的原理跟单核是一致的。
- 当M0处理完所有的协程,而M1还未处理完,M0会取走M1的一半数量未处理的协程。