自从开始使用 Go 语言,到现在也有一年多了,虽不算精通,但也算小有理解。在这里简单记录一下我的心得(其实是学习别人的心得)

goroutine,Go 语言中 cpu 运行的最小单元,与 lua 携程类似,只是叫法和调度方式不同,而 Go-runtime 是如何调度这些 goroutine 将大大小小“工作”安排的明明白白呢,这个就是我们本章要解释的内容。

先提出 3 个组件,分别是 M(Machine)、P(Processor)、G(Goroutinue),他们的相关定义和结构都可以在 Go 语言源码(runtime.h)中看到

  • M(Machine)内核级线程,一个 M 就代表一个真实线程,所有的任务都是跑在 M 之上,数据结构中维护有当前管理的 P、当前执行中的 G 信息和上下文、缓存内存计数器等等...
  • P(Processor)处理器,主要用途是对 goroutinue 进行管理并执行,数据结构中维护有 goroutinue 列表、当前执行中的 G 信息。看到这里可能会感觉到 P 的角色与 M 很相似,但其实 M 是真实的执行者,P 更像是执行和管理个工具,下面我们会讲到两者关系
  • G (Goroutine) 携程,任务的最小单位,数据结构记录自身的上下文信息

这 3 个组件对于调度算法非常重要,整个调度的过程都由这三者进行管理(这里给出一张图,也是我想要写本章的动力,形象可爱又具体)

地鼠(M)用小车(P)运送并加工砖头(G)

这里其实如果有了解过调度概念的,应该已经有了基本的头绪,下面我们深入了解一下细节

1,启动程序(编译原理不解释了,本文只讲 Go 进程的运行调度)

执行可执行文件后,并不会直接去执行 main 函数,而是调度初始化 runtime 模块,并创建第一个 G 执行 main 函数,所以说 G 是 Go 语言的执行单元,所有的执行都是以 Goroutinue 为载体跑在 cpu 上的。那之前说过, Goroutinue 想要跑起来,必须依赖 P/M,否则只凭一块转,怎么可能将自己加工好并运出去呢?所以这里了解一下 runtime 的初始化过程(巴拉巴拉,包含参数,os,hash初始化),本文重点是调度,所以看看调度器初始化的过程就是根据用户的 cpu 核数,建立好多个 Process,然后直接将第一个 G 丢到一个 Global 队列(砖堆)中。

那么刚才也说了,最终的执行者还是要靠地鼠(M、CPU)来干苦力,在程序启动后,主函数执行之前,会创建出第一个 M(系统监控 sysmon,万恶的资本家),Go 语言的阻塞调度、GC 机制、调度算法都离不开这个 M。

sysmon 发现当前并没有 M 干活(M 不够,有空闲的 P 并且有 G)就会招聘(创建)一个地鼠(M)并将有砖块(G)的小车(P)绑定起来,到这里,一个 M/P/G 的关系就建立起来了,此时边开始由 M 去执行 P 中的 G 了。而每次代码中我们执行 go func,都会创建一个新的 goroutinue 加入到 Global 队列。

2, 调度算法

为什么存在调度,就是为了并发更高,资源利用率更高,那如果一个地鼠的砖块都干完了,当然不能停下来,而是去 Global 队列(砖堆)取,还是没有,就去其他地鼠小车里抢砖干(可以看出这里是地鼠自主的去找任务做,说明是算绩效的)。如果两者都没有,尝试几次后,就会选择去睡觉(挂起),不去消耗资源了。以上其实就是 Go 调度算法的主流程,当然一个程序中不可能不可能一切是这么自然,总有砖块是难渣滓(channel 操作,io 阻塞),goroutine 在调用时,就会变成 waiting 状态,这样地鼠就先去干后面的活了,等待 goroutine 的事件回调触发后,会重新加入到就绪队列中等待执行;而如果是需要系统调用的情况,那么地鼠(M)就必须在旁边等待,而当前小车(P)会与地鼠(M)解绑,由 sysmon 重新创建一个地鼠(M)去干剩下的活。这一整套调度算法被称为 work stealing,这种调度算法现在还经常应用于各大系统中。

3,抢占调度

假设一个 Goroutine 不停的 for 循环,因为 Go 语言没有根据时间片调度方式,为每个 G 设定执行时间,那他就会一直循环下去吗?当然不会,开始说过的 sysmon 就是专门搞定这些刺头,在多次监控后,发现当前 G 不停占用资源,会将其从地鼠(M)手里抢过来,强制放入就绪队列,这样就没有人能搞事情咯。

本文仅仅是简单记录一下 goroutine 的调度大纲,其中涉及到操作系统的一些细节问题(G 上下文恢复,抢占调度,异步阻塞)这里就不一一详解了。有兴趣的程序媛们可以自行找相关的资料学习一下

goroutine 调度算法的更多相关文章

  1. Golang学习笔记:goroutine

    1.goroutine goroutine是go语言的并发体.在go语言里面能使用go关键字来实现并发. go func() 1.1 概念介绍 goroutine本质上是协程,我刚刚学习的时候就粗略地 ...

  2. 103 - kube-scheduler源码分析 - 调度算法-寻找predicates和priorities

    scheduler的主要逻辑是predicate和priority,前者回答哪些节点可以运行pod的问题,后者回答哪个节点更合适运行pod的问题.今天我们的任务是:从主函数出发,寻找predicate ...

  3. goroutine 和线程的区别

    好久没写点儿啥了,强行更新一下. 1,从使用上讲 1,goroutine 比线程更轻量级,可以创建十万.百万不用担心资源问题. 2,goroutine 和 chan 搭配使用,实现多线程.高并发 实现 ...

  4. goroutine与调度器

    29 November 2013 by skoo 我们都知道Go语言是原生支持语言级并发的,这个并发的最小逻辑单元就是goroutine.goroutine就是Go语言提供的一种用户态线程,当然这种用 ...

  5. Goroutine并发调度模型深度解析之手撸一个协程池

    golanggoroutine协程池Groutine Pool高并发 并发(并行),一直以来都是一个编程语言里的核心主题之一,也是被开发者关注最多的话题:Go语言作为一个出道以来就自带 『高并发』光环 ...

  6. 以goroutine为例看协程的相关概念

    转自  http://wangzhezhe.github.io/blog/2016/02/17/golang-scheduler/ 基本上是网上相关文章的梳理,初衷主要是想了解下golang中的gor ...

  7. Golang/Go goroutine调度器原理/实现【原】

    Go语言在2016年再次拿下TIBOE年度编程语言称号,这充分证明了Go语言这几年在全世界范围内的受欢迎程度.如果要对世界范围内的gopher发起一次“你究竟喜欢Go的哪一点”的调查,我相信很多Gop ...

  8. Goroutine调度器

    前言 并发(并行)一致都是编程语言的核心主题,不同于其他语言,例如C/C++语言用户序自行借助pthread创建线程,Golang天然就给出了并发解决方案:goroutine. Goroutine 写 ...

  9. TODO:Go语言goroutine和channel使用

    TODO:Go语言goroutine和channel使用 goroutine是Go语言中的轻量级线程实现,由Go语言运行时(runtime)管理.使用的时候在函数前面加"go"这个 ...

随机推荐

  1. YOLO---Darknet下的学习笔记 V190319

    YOLO---Darknet下的学习笔记 @WP 20190319 很久没有用YOlO算法了,今天又拿过来玩玩.折腾半天,才好运行通的,随手记一下: 一是,终端下的使用.二是,python接口的使用. ...

  2. 4'.deploy.prototxt

    1: 神经网络中,我们通过最小化神经网络来训练网络,所以在训练时最后一层是损失函数层(LOSS), 在测试时我们通过准确率来评价该网络的优劣,因此最后一层是准确率层(ACCURACY). 但是当我们真 ...

  3. tnsnames.ora配置细节注意

    WXPT = (DESCRIPTION = (ADDRESS_LIST = (ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.1.250)(PORT = 1521) ...

  4. RxJava——响应式编程

    自从06年开始,Rxandroid公司项目中陆续就开始使用它了,而它的基础是由Rxjava演变过来的,如今它也是越来越被广泛使用在商业项目中了,而做为"专业"的自己还是一直对它一知 ...

  5. Linux文件系统之mv(重命名/移动文件)

    mv(move)命令 输入man mv,了解到mv命令是用于移动或重命名文件 语法 mv [options] source dest mv [options] source... directory ...

  6. vscode调整字体大小

    在vscode中,通过setting>User>Text Editor>Font可以调整字体大小,但是这里只是调整右侧的代码编辑区域的字体,左侧的侧边栏确无法调整字体大小,找了很久都 ...

  7. http协议。会话控制cookie、session

    http协议是无状态的协议.每次访问页面的http协议都是独立的,正是因为http协议是无状态的,所以导致访问一个页面后再去访问另一个页面的时候,一些数据会消失,比如:用户的登录信息就会消失.那么怎么 ...

  8. 校验正确获取对象或者数组的属性方法(babel-plugin-idx/_.get)

    背景: 开发中经常遇到取值属性的时候,需要校验数值的有效性. 例如: 获取props对象里面的friends属性 props.user && props.user.friends &a ...

  9. 【线性代数】2-5:逆(Inverse)

    title: [线性代数]2-5:逆(Inverse) toc: true categories: Mathematic Linear Algebra date: 2017-09-11 20:00:1 ...

  10. 第72节:Java中的数组

    https://www.jianshu.com/p/9ad176caa5bc