调度器GMP简记

本文详细解析了GMP调度器中的goroutine、processor和thread(M)之间的关系,强调了goroutine的内存效率和调度灵活性,以及M如何通过P进行任务分发和线程复用。gofunc()的调度流程也做了介绍,包括G的创建、存储和执行过程,以及调度器的生命周期管理。
摘要由CSDN通过智能技术生成

调度器 GMP:线程是有pcb的内核级线程,协程是用户级线程。goroutinue占内存更小(几kb)调度更灵活(runtime调度)

  • 三个主要组件:goroutine(G)、processor(P )、thread(M),P是新的GMP引入的组件,包含运行goroutine的资源,M是运行goroutine的实体。Goroutine 调度器和 OS 调度器是通过 M 结合起来的,每个 M 都代表了 1 个内核线程,OS 调度器负责把内核线程分配到 CPU 的核上执行
  • 其他组件:
    • 全局队列:存放等待运行的G
    • P的本地队列:P的存放等待的G,新建G时会优先放在自己的本地队列里 。(本地队列若满将前一半的G放在全局队列中)
    • P列表:所有的P启动时创建保存在数组里
  • M运行任务要获得P,从P本地队列获取P 队列为空时,M 会尝试从全局队列一批 G 放到 P 的本地队列,或从其他 P 的本地队列一半放到自己 P 的本地队列。M 运行 G,G 执行之后,M 会从 P 获取G,不断重复。创建G时,运行的G会尝试唤醒其他空闲的P和M组合执行,若绑定后P本地队列没有G则会自旋线程(没有G但为运行态线程不断寻找G),自旋线程数量有限为了线程复用。
  • go func()调度流程
    • 通过 go func () 来创建一个 goroutine;
    • 有两个存储 G 的队列,一个是局部调度器 P 的本地队列、一个是全局 G 队列。新创建的 G 会先保存在 P 的本地队列中,如果 P 的本地队列已经满了就会保存在全局的队列中;
    • G 只能运行在 M 中,一个 M 必须持有一个 P,M 与 P 是 1:1 的关系。M 会从 P 的本地队列弹出一个可执行状态的 G 来执行,如果 P 的本地队列为空,就会向其他的 MP 组合偷取一个可执行的 G 来执行;
    • 一个 M 调度 G 执行的过程是一个循环机制;
    • 当 M 执行某一个 G 时候如果发生了 syscall 或则其余阻塞操作,M 会阻塞,如果当前有一些 G 在执行,runtime 会把这个线程 M 从 P 中摘除 (detach),再创建一个新的操作系统的线程 (如果有空闲的线程可用就复用空闲线程) 来服务于这个 P;
    • 当 M 系统调用结束时候,这个 G 会尝试获取一个空闲的 P 执行,并放入到这个 P 的本地队列。如果获取不到 P,那么这个线程 M 变成休眠状态, 加入到空闲线程中,然后这个 G 会被放入全局队列中。
  • 调度器的生命周期:创建第一个M0->创建第一个G0->调度初始化->创建main()的g->启动M0->M绑定P->M通过P获取到G(若获取不到G则令M休眠等待被唤醒然后再绑定)->M设置G环境->M执行G->G退出->M通过P获取G->…
  • M0是启动程序后编号为0的主线程,不在heap,M0负责初始化启动第一个M。G0每个M启动时创建的第一个g,G0仅用于负责调度
  • 28
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值