作者:林冠宏 / 指尖下的幽灵

掘金:https://juejin.im/user/587f0dfe128fe100570ce2d8

博客:http://www.cnblogs.com/linguanh/

GitHub : https://github.com/af913337456/

腾讯云专栏: https://cloud.tencent.com/developer/user/1148436/activities


前序

正确地认识 G , M , P 三者的关系,能够对协程的调度机制有更深入的理解! 本文将会完整介绍完 go 协程的调度机制,包含:

  • 调度对象的主要组成
  • 各对象的关系 与 分工
  • gorutine 协程是如何被执行的
  • 内核线程 sysmon 对 gorutine 的管理
  • gorutine 协程中断挂起 与 恢复
  • GOMAXPROCS 如何影响 go 的并发性能

目录

调度器的三个基本对象:

Golang 简称 Go,Go 的协程(goroutine) 和我们常见的线程(Thread)一样,拥有其调度器。

  • G (Goroutine),代表协程,也就是每次代码中使用 go 关键词时候会创建的一个对象
  • M (Work Thread),工作线程
  • P (Processor),代表一个处理器,又称上下文

G-M-P三者的关系与特点:

  • 每一个运行的 M 都必须绑定一个 P,线程M 创建后会去检查并执行G (goroutine)对象
  • 每一个 P 保存着一个协程G 的队列
  • 除了每个 P 自身保存的 G 的队列外,调度器还拥有一个全局的 G 队列
  • M 从队列中提取 G,并执行
  • P 的个数就是GOMAXPROCS(最大256),启动时固定的,一般不修改
  • M 的个数和 P 的个数不一定一样多(会有休眠的M 或 P不绑定M )(最大10000)
  • P 是用一个全局数组(255)来保存的,并且维护着一个全局的 P 空闲链表

局部G队列与全局G队列的关系

  • 全局G任务队列会和各个本地G任务队列按照一定的策略互相交换。没错,就是协程任务交换
  • G任务的执行顺序是,先从本地队列找,本地没有则从全局队列
  • 转移
    • 局部与全局,全局G个数 / P个数
    • 局部与局部,一次性转移一半

Gorutine从入队到执行

  1. 当我们创建一个G对象,就是 gorutine,它会加入到本地队列或者全局队列
  2. 如果还有空闲的P,则创建一个M 绑定该 P ,注意!这里,P 此前必须还没绑定过M 的,否则不满足空闲的条件。细节点:
    1. 先找到一个空闲的P,如果没有则直接返回
    2. P 个数不会占用超过自己设定的cpu个数
    3. P 在被 M 绑定后,就会初始化自己的 G 队列,此时是一个空队列
    4. 注意这里的一个点
      • 无论在哪个 M 中创建了一个 G,只要 P 有空闲的,就会引起新 M 的创建
      • 不需考虑当前所在 M 中所绑的 P 的 G 队列是否已满
      • 新创建的 M 所绑的 P 的初始化队列会从其他 G 队列中取任务过来
    5. 这里留下第一个问题:

      如果一个G任务执行时间太长,它就会一直占用 M 线程,由于队列的G任务是顺序执行的,其它G任务就会阻塞,如何避免该情况发生? --①

  3. M 会启动一个底层线程循环执行能找到的 G 任务。这里的寻找的 G 从下面几方面找:
    • 当前 M 所绑的 P 队列中找
    • 去别的 P 的队列中找
    • 去全局 G 队列中找
  4. G任务的执行顺序是,先从本地队列找,本地没有则从全局队列找
  5. 程序启动的时候,首先跑的是主线程,然后这个主线程会绑定第一个 P
  6. 入口 main 函数,其实是作为一个 goroutine 来执行

解答问题-①

协程的切换时间片是10ms,也就是说 goroutine 最多执行10ms就会被 M 切换到下一个 G。这个过程,又被称为 中断,挂起

原理:

go程序启动时会首先创建一个特殊的内核线程 sysmon,用来监控和管理,其内部是一个循环:

  1. 记录所有 P 的 G 任务的计数 schedtick,schedtick会在每执行一个G任务后递增

  2. 如果检查到 schedtick 一直没有递增,说明这个 P 一直在执行同一个 G 任务,如果超过10ms,就在这个G任务的栈信息里面加一个 tag 标记

  3. 然后这个 G 任务在执行的时候,如果遇到非内联函数调用,就会检查一次这个标记,然后中断自己,把自己加到队列末尾,执行下一个G

  4. 如果没有遇到非内联函数 调用的话,那就会一直执行这个G任务,直到它自己结束;如果是个死循环,并且 GOMAXPROCS=1 的话。那么一直只会只有一个 P 与一个 M,且队列中的其他 G 不会被执行!

例子,下面的这段代码,hello world 不会被输出


func main(){
runtime.GOMAXPROCS(1)
go func(){
fmt.Println("hello world")
// panic("hello world") // 强制观察输出
}()
go func(){
for {
// fmt.Println("aaa") // 非内联函数,这行注释打开,将导致 hello world 的输出
}
}()
select {}
}

中断后的恢复

  1. 中断的时候将寄存器里的栈信息,保存到自己的 G 对象里面
  2. 当再次轮到自己执行时,将自己保存的栈信息复制到寄存器里面,这样就接着上次之后运

GOMAXPROCS--性能调优

看完上面的内容,相信你已经知道,GOMAXPROCS 就是 go 中 runtime 包的一个函数。它设置了 P 的最多的个数。这也就直接导致了 M 最多的个数是多少,而 M 的个数就决定了各个 G 队列能同时被多少个 M 线程来进行调取执行!

故,我们一般将 GOMAXPROCS 的个数设置为 CPU 的核数,且需要注意的是:

  • go 1.5 版本之前的 GOMAXPROCS 默认是 1
  • go 1.5 版本之后的 GOMAXPROCS 默认是 Num of cpu

Golang 的 协程调度机制 与 GOMAXPROCS 性能调优的更多相关文章

  1. Openresty Lua协程调度机制

    写在前面 OpenResty(后面简称:OR)是一个基于Nginx和Lua的高性能Web平台,它内部集成大量的Lua API以及第三方模块,可以利用它快速搭建支持高并发.极具动态性和扩展性的Web应用 ...

  2. Golang 协程调度

    一.线程模型 N:1模型,N个用户空间线程在1个内核空间线程上运行.优势是上下文切换非常快但是无法利用多核系统的优点. 1:1模型,1个内核空间线程运行一个用户空间线程.这种充分利用了多核系统的优势但 ...

  3. Golang 之协程详解

    转自:https://www.cnblogs.com/liang1101/p/7285955.html 一.Golang 线程和协程的区别 备注:需要区分进程.线程(内核级线程).协程(用户级线程)三 ...

  4. 剑指Offer——知识点储备-故障检测、性能调优与Java类加载机制

    剑指Offer--知识点储备-故障检测.性能调优与Java类加载机制 故障检测.性能调优 用什么工具可以查出内存泄露 (1)MerroyAnalyzer:一个功能丰富的java堆转储文件分析工具,可以 ...

  5. 故障检测、性能调优与Java类加载机制

    故障检测.性能调优与Java类加载机制 故障检测.性能调优 用什么工具可以查出内存泄露 (1)MerroyAnalyzer:一个功能丰富的java堆转储文件分析工具,可以帮助你发现内存漏洞和减少内存消 ...

  6. GoLang之协程

    GoLang之协程 目前,WebServer几种主流的并发模型: 多线程,每个线程一次处理一个请求,在当前请求处理完成之前不会接收其它请求:但在高并发环境下,多线程的开销比较大: 基于回调的异步IO, ...

  7. GO GMP协程调度实现原理 5w字长文史上最全

    1 Runtime简介 Go语言是互联网时代的C,因为其语法简洁易学,对高并发拥有语言级别的亲和性.而且不同于虚拟机的方案.Go通过在编译时嵌入平台相关的系统指令可直接编译为对应平台的机器码,同时嵌入 ...

  8. 图解Go协程调度原理,小白都能理解

    阅读本文仅需五分钟,golang协程调度原理,小白也能看懂,超实用. 什么是协程 对于进程.线程,都是有内核进行调度,有CPU时间片的概念,进行抢占式调度.协程,又称微线程,纤程.英文名Corouti ...

  9. go协程调度

    目录 前言 1. 线程池的缺陷 2.Goroutine 调度器 3.调度策略 3.1 队列轮转 3.2 系统调用 3.3 工作量窃取 4.GOMAXPROCS设置对性能的影响 参考 前言 Gorout ...

随机推荐

  1. Vue(十四)过渡(动画)

    过渡(动画) 1. 简介 Vue 在插入.更新或者移除 DOM 时,提供多种不同方式的应用过渡效果 本质上还是使用CSS3动画:transition.animation 2. 基本用法 使用trans ...

  2. 微信小程序中遮罩层的滚动穿透问题

    如果弹出层没有滚动事件: <view wx:if="{{alert}}" catchtouchmove="myCatchTouch"> <te ...

  3. PowerBI发布到网页

    如果网页当中需要嵌入PowerBI的报表,可以在PowerBI当中生成链接,然后网页或者博客当中插入这一段html代码. 以下是PowerBI生产网页链接的示例,并且在博客的最后也插入了PowerBI ...

  4. arcgis 获得工具箱工具的个数

    import arcgisscripting import string; gp = arcgisscripting.create(9.3); ##多少个工具箱 toolboxes = gp.list ...

  5. mount 命令用法

    mount 功能: 加载指定的文件系 统:mount可将指定设备中指定的文件系统加载到 Linux目录下(也就是装载点).可将经常使用的设备写入文件/etc/fastab,以使系 统在每次启动时自动加 ...

  6. 挖矿病毒 qW3xT.2 最终解决方案

    转自:https://blog.csdn.net/hgx13467479678/article/details/82347473 1,cpu 100%, 用top 查看cpu100 2,删掉此进程 c ...

  7. 【黑魔法】Covering Indexes、STRAIGHT_JOIN

    今天给大家介绍两个黑魔法,这都是压箱底的法宝.大家在使用时,一定要弄清他们的适用场景及用法,用好了,就是一把开天斧,用不好那就是画蛇添足.自从看过耗子哥(左耳朵耗子)的博客,都会给对相应专题有兴趣的小 ...

  8. Swagger Annotation 详解(建议收藏)

    转载:https://www.jianshu.com/p/b0b19368e4a8 在软件开发行业,管理文档是件头疼的事.不是文档难于撰写,而是文档难于维护,因为需求与代码会经常变动,尤其在采用敏捷软 ...

  9. DroneCI启用privileged

    https://www.aliyun.com/jiaocheng/123155.html?spm=5176.100033.2.5.EIV4p6 drone的服务需要配置DRONE_ADMIN环境变量, ...

  10. github远程建了分支,本地看不到的问题

    原因:Git  branch -a 查看的是本地仓库的所有分支  远程新建的没有同步前 就是看不到 解决:$git checkout master   //首先切到master分支 $git pull ...