Go协程调度

1、什么是协程?

对于进程、线程,都是有内核进行调度,有CPU时间片的概念,进行抢占式调度。协程,又称微线程,纤程。英文名Coroutine。协程的调用有点类似子程序,如程序A调用了子程序B,子程序B调用了子程序C,当子程序C结束了返回子程序B继续执行之后的逻辑,当子程序B运行结束了返回程序A,直到程序A运行结束。但是和子程序相比,协程有挂起的概念,协程可以挂起跳转执行其他协程,合适的时机再跳转回来

2、线程调度原理

N:1模型,多个用户空间线程在1个内核空间线程上运行。优势是上下文切换非常快,因为这些线程都在内核态运行,但是无法利用多核系统的优点
1:1模型,1个内核空间线程运行一个用户空间线程。这种充分利用了多核系统的优势但是上下文切换非常慢,因为每一次调度都会在用户态和内核态之间切换。POSIX线程模型(pthread)就是这么做的。
M:N模型,内核空间开启多个内核线程,一个内核空间线程对应多个用户空间线程。效率非常高,但是管理复杂

3、goroutine调度原理

本质上goroutine就是协程,但是完全运行在用户态,借鉴了M:N模型。如下图
在这里插入图片描述
相比其他语言,golang采用了MPG模型管理协程,更加高效,但是管理非常复杂。
M:内核级线程
G:代表一个goroutine
P:Processor,处理器,用来为G提供必须的资源,管理和执行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。

假设我们的主机是单核的,那么协程运行图是这样:
在这里插入图片描述

4、运行机制

M:
1、M创建时会放入全局M列表,运行时系统会为这个M专门创建一个新的内核线程并与之关联
全局M队列作用主要是(1)获取所有M的信息,(2)防止M被当作垃圾回收掉
2、M与P完成关联,并寻找可运行的G
3、M与G锁定在一起,M运行这个G

注:
(1)M被停止(垃圾回收)会被放入空闲M列表,需要时先从该列表中获取
(2)M的最大数量是可以设置的,初始值是10000
(3)如果M设置的值比实际M的数量小时,运行系统就会立即处罚panic错误
P:
1、P的数量即为可运行G的队列的数量。一个G在被启用后,会先被追加到某个P的可运行队列中。一个P只能与一个M关联在一起,才会使其可运行G队列中的G有机会运行。
2、P的默认值与当前CPU的总核心数相同,可被设置,但是不能超过硬性上限(256)
3、当一个P不再与任何M关联的时候,运行系统会把它放入空闲P列表,P放入空闲P列表可运行的G列表必须为空。其可运行G列表中的G都会被转移到调度器的可运行G列表,而自由G列表中的G被转移到调度器的自由G列表
4、P包含一个可运行G队列,一个自由G列表。随着完成的G增多,自由G列表增加到一定长度会把其中部分G转移到调度器的自由G列表中。
5、go语句欲启动一个G时,会从调度器的自由G列表中获取一个现成的G,来封装这个go语句携带的函数。只有自由G列表为空时,才会创建新的G,这样做提高了G的复用率
G:
1、先从本地P的自由G列表和调度器的自由G列表中获取可用的G,如果没有获取到,就新建一个G.
2、系统有一个全局的G列表,新建的G会第一时间加入该列表中。该列表的作用是集中存放当前运行时系统中的所有G的指针。
学习链接:https://www.cnblogs.com/secondtonone1/p/11803961.html

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值