goland goroutine协程调度

线程与协程

我们以Java线程为例。

熟悉Java的朋友肯定知道线程,一个Java JVM thread对应一个os thread,是1:1的关系。但是在goland中情况就不是这样的了,多个goroutine可以运行在一个os thread上,是1:n的关系。可以简单理解为goroutine是go对类Java中的thread进行了一层封装。

线程与协程的运行基础都是os thread,最底层的任务调度都是基于os 本身的任务切换。

Java线程调度:
在这里插入图片描述
goroutine调度:
在这里插入图片描述那么Java线程与go协程的异同点在哪里呢?
主要提现在内存、创建、调度三个方面

  • 创建一个go goroutine,内存消耗大概在2KB左右,由于go的线程栈本身是支持动态扩容的,后续如果goroutine栈容量不足,可以自动扩容。而创建一个thread是os 开销,一个thread需要消耗内存2-3MB。如果是在高并发场景下,不停创建新的thread的成本是非常高昂的,所以Java中一版会使用线程池来规避创建thread的性能以及内存消耗问题
  • 如上图所示,Java线程是与os 线程一一对应,属于内核级交互,其线程调度归属于os。os在切换线程时需要保存线程当前快照,当该线程再度被唤起时需要恢复快照,需要保存各种寄存器数据。而goroutine则是由go runtime进行管理和调度,属于用户层交互,其goroutine切换效率远高于thread

go scheduler

g、m、p

既然goroutine相较于thread效率明显,那么其scheduler又是如何进行调度的呢?
有三个基础的结构体来实现 goroutines 的调度:g,m,p。
这三个结构体均在runtime2中进行定义:
在这里插入图片描述

我们先来看看g:
在这里插入图片描述
根据结构体g的注释以及参数定义,可以看出g跟栈内容相关。

下面是m的定义:
在这里插入图片描述
在m中我们可以看到关联了很多g,还有thread-local等相关信息

最后看看p的定义:
在这里插入图片描述
在p的定义中我们可以看到定义了一个run的运行队列。

根据源码中相关类型定义,我们可以对p、m、g做出如下总结:

  • g 代表一个 goroutine,它包含:表示 goroutine 栈的一些字段,指示当前 goroutine 的状态,指示当前运行到的指令地址,也就是 PC 值等。
  • m 表示内核线程,g运行在m上,也即一个goroutine需要在一个m上才能运行起来
  • p 代表一个虚拟的 Processor,它维护一个处于 Runnable 状态的 g 队列

其三者之间的关系大体是:m需要拿到p里面维护的g队列后,从g队列里面拿出一个g,在m上运行

对于 M 来说,P 提供了相关的执行环境(Context),如内存分配状态,任务队列等。P 的数量就是程序可最大可并行的 G 的数量(前提:物理CPU核数 >= P的数量),由用户设置的 GOMAXPROCS 决定。M 数量不定,但同时只有 P 个 M 在执行,为了防止创建过多系统线程导致系统调度出现问题,目前默认最大限制10000个。

运行队列

  • 本地可运行队列(LRQ):P本身维护的G队列,即为LRQ,维护的是这个P本身可运行的所有 goroutine。
  • 全局可运行队列(GRQ):GRQ 存储全局的可运行 goroutine,这些 goroutine 还没有分配到具体的 P。

调度机制

那么go scheduler是如何对goroutine进行调度的呢?
当go应用运行起来之后,go会给每个cpu核心分配一个P(processer),同时每个cpu核心会创建出一个M(也即内核thread),同时将这个M分配给这个P。当然M本身的调度还是有os自己进行的。
当go应用初始化并运行起来之后,就会产生一个G,这个G会放入P的队列,最终由M执行这个G。

由此我们可以看下,goroutine的调度,实际是由M进行管理的。

假设我们CPU为双核心,现在启动一个go应用,并且go应用内部启动了n个协程进行任务处理:
在这里插入图片描述

调度算法

  • 每个 P 维护一个本地队列;
  • 当一个 G 被创建后,放入当前 P 的本地队列中,如果队列已满,放入全局队列;
  • 当 M 执行完一个 G 后,会在 当前 P 的队列中取出新的 G,队列为空则在全局队列中加锁获取;
  • 如果全局队列也为空,则去其他的 P 的队列中偷出一半的 G,放入自己的本地队列。

调度器部分的代码主要集中在 runtime2.go 与 proc.go 这两个文件中。

源码解析:
在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值