调度器

主要基于三个基本对象上,G,M,P(定义在源码的src/runtime/runtime.h文件中)

  1. G代表一个goroutine对象,每次go调用的时候,都会创建一个G对象
  2. M代表一个线程,每次创建一个M的时候,都会有一个底层线程创建;所有的G任务,最终还是在M上执行
  3. P代表一个处理器,每一个运行的M都必须绑定一个P,就像线程必须在么一个CPU核上执行一样

P的个数就是GOMAXPROCS(最大256),启动时固定的,一般不修改;
M的个数和P的个数不一定一样多(会有休眠的M或者不需要太多的M)(最大10000);每一个P保存着本地G任务队列,也有一个全局G任务队列;
如下图所示

 
 

全局G任务队列会和各个本地G任务队列按照一定的策略互相交换(满了,则把本地队列的一半送给全局队列)
P是用一个全局数组(255)来保存的,并且维护着一个全局的P空闲链表

每次go调用的时候,都会:

  1. 创建一个G对象,加入到本地队列或者全局队列
  2. 如果还有空闲的P,则创建一个M
  3. M会启动一个底层线程,循环执行能找到的G任务
  4. G任务的执行顺序是,先从本地队列找,本地没有则从全局队列找(一次性转移(全局G个数/P个数)个,再去其它P中找(一次性转移一半),
  5. 以上的G任务执行是按照队列顺序(也就是go调用的顺序)执行的。(这个地方是不是觉得很奇怪??)

对于上面的第2-3步,创建一个M,其过程:

  • 先找到一个空闲的P,如果没有则直接返回,(哈哈,这个地方就保证了进程不会占用超过自己设定的cpu个数)
  • 调用系统api创建线程,不同的操作系统,调用不一样,其实就是和c语言创建过程是一致的,(windows用的是CreateThread,linux用的是clone系统调用)
  • 然后创建的这个线程里面才是真正做事的,循环执行G任务

那就会有个问题,如果一个系统调用或者G任务执行太长,他就会一直占用这个线程,由于本地队列的G任务是顺序执行的,其它G任务就会阻塞了,怎样中止长任务的呢?

这样滴,启动的时候,会专门创建一个线程sysmon,用来监控和管理,在内部是一个循环:

  1. 记录所有P的G任务计数schedtick,(schedtick会在每执行一个G任务后递增)
  2. 如果检查到 schedtick一直没有递增,说明这个P一直在执行同一个G任务,如果超过一定的时间(10ms),就在这个G任务的栈信息里面加一个标记
  3. 然后这个G任务在执行的时候,如果遇到非内联函数调用,就会检查一次这个标记,然后中断自己,把自己加到队列末尾,执行下一个G
  4. 如果没有遇到非内联函数(有时候正常的小函数会被优化成内联函数)调用的话,那就惨了,会一直执行这个G任务,直到它自己结束;如果是个死循环,并且GOMAXPROCS=1的话,恭喜你,夯住了!亲测,的确如此

对于一个G任务,中断后的恢复过程:

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

但是还有一个问题,就是系统启动的过程

  1. 系统启动的时候,首先跑的是主线程,那第一个M应该就是主线程吧(按照C语言的理解,嘿嘿),这里叫M1,可以看前面的图
  2. 然后这个主线程会绑定第一个P1
  3. 咱们写的main函数,其实是作为一个goroutine来执行的
  4. 也就是第一个P1就有了一个G1任务,然后第一个M1就执行这个G1任务(也就是main函数),创建这个G1的时候不用创建M了,因为已经有了M1
  5. 这个main函数里面所有的goroutine,都绑定到当前的M1所对应的P1上
  6. 然后创建main里的goroutine的时候(比如G2),就会创建新的M2,新的M2里的初始P2的本地任务队列是空的,会从P1里面取一些过来,哈哈
  7. 这样两个M1,M2各自执行自己的G任务,再依次往复,这下就圆满了

综上:
所以goroutine是按照抢占式调度的,一个goroutine最多执行10ms就会换作下一个
这个和目前主流系统的的cpu调度类似(按照时间分片)

windows:20ms

linux:5ms-800ms

注意:
在Golang中编译器也会尝试进行内联,将小函数直接复制并编译,为了内联,尽量消除编译器无法侦测的dead code,利用gobuild -gcflags=-m编译命令可以查看程序内联状态,不得不说golang的编译工具链还是很强大的,十分有利于程序的优化。

[转帖]go的调度机制.的更多相关文章

  1. (笔记)Linux内核学习(十一)之I/O层和I/O调度机制

    一 块I/O基本概念 字符设备:按照字符流的方式被有序访问的设备.如串口.键盘等. 块设备:系统中不能随机(不需要按顺序)访问固定大小的数据片(chunk 块)的设备. 如:硬盘.软盘.CD-ROM驱 ...

  2. quartz集群调度机制调研及源码分析---转载

    quartz2.2.1集群调度机制调研及源码分析引言quartz集群架构调度器实例化调度过程触发器的获取触发trigger:Job执行过程:总结:附: 引言 quratz是目前最为成熟,使用最广泛的j ...

  3. 定时组件quartz系列<三>quartz调度机制调研及源码分析

    quartz2.2.1集群调度机制调研及源码分析引言quartz集群架构调度器实例化调度过程触发器的获取触发trigger:Job执行过程:总结:附: 引言 quratz是目前最为成熟,使用最广泛的j ...

  4. (1)quartz集群调度机制调研及源码分析---转载

    quartz2.2.1集群调度机制调研及源码分析 原文地址:http://demo.netfoucs.com/gklifg/article/details/27090179 引言quartz集群架构调 ...

  5. quartz群调查调度机制和源代码分析

    pageId=85056282#quartz集群调度机制调研及源代码分析-quartz2.2.1集群调度机制调研及源代码分析" style="color:rgb(59,115,17 ...

  6. Linux进程组调度机制分析【转】

    转自:http://oenhan.com/task-group-sched 又碰到一个神奇的进程调度问题,在系统重启过程中,发现系统挂住了,过了30s后才重新复位,真正系统复位的原因是硬件看门狗重启的 ...

  7. [k8s]k8s的控制层kubelet+docker配合调度机制(k8架构)

    意外停掉一台node的kubelet,发现调度有问题,研究了下调度的细节 k8s架构 控制层- kubelet(配合节点docker工作) 数据层- kube-proxy 逻辑图: object 参考 ...

  8. Golang 的 协程调度机制 与 GOMAXPROCS 性能调优

    作者:林冠宏 / 指尖下的幽灵 掘金:https://juejin.im/user/587f0dfe128fe100570ce2d8 博客:http://www.cnblogs.com/linguan ...

  9. [转]golang的goroutine调度机制

    golang的goroutine调度机制 版权声明:本文为博主原创文章,未经博主允许不得转载.   目录(?)[-] 一直对goroutine的调度机制很好奇最近在看雨痕的golang源码分析基于go ...

随机推荐

  1. 洛谷 2953 [USACO09OPEN]牛的数字游戏Cow Digit Game

    洛谷 2953 [USACO09OPEN]牛的数字游戏Cow Digit Game 题目描述 Bessie is playing a number game against Farmer John, ...

  2. Ubuntu14.04 64位机上安装cuda8.0+cudnn5.0操作步骤

    查看Ubuntu14.04 64位上显卡信息,执行: lspci | grep -i vga lspci -v -s 01:00.0 nvidia-smi 第一条此命令可以显示一些显卡的相关信息:如果 ...

  3. CF 1138 E. Museums Tour

    E. Museums Tour 链接 分析: 按时间建出分层图,每个点形如(u,t),表示u在在t个时刻的点,tarjan缩点.每个强连通分量中的点都能经过,然后DAG上dp. 代码: #includ ...

  4. Java学习技术图

    最近,在研究docker,作为一个程序员,要想提高自己的竞争力,必须时刻保持学习的态度,技多不压身:发现从事Java工作以来,买了很多书,也逛了很多技术贴,技术的平面宽度是不断的延伸,有些是工作中需要 ...

  5. 处于同一域中的两台SQL Server 实例无法连接

    处于同一个域中的两台Sql server 实例无法连接,报的错误信息如下: A network-related or instance-specific error occurred while es ...

  6. Linux入门基础(五):Linux管道,重定向,文本处理

    多命令协作:管道及重定向 在Linux中,大多数命令都很简单,每个命令往往只实现一个或几个很简单功能. 我们可以通过将不同功能的命令组合起来一起使用,达到完成某个复杂功能的目的 CLI下几乎所有的命令 ...

  7. Jenkins远程测试

    Jenkins远程测试 网络测试,如,selenium 测试可以通过主从和 selenium 套件插件远程安装在机器上运行.下列步骤显示了如何运行使用此配置来进行远程测试. 第1步 - 确保主从配置到 ...

  8. [环境配置]Ubuntu 16.04+CUDA 9.0+OpenCV 3.2.0下编译基于Caffe的MobileNet-SSD踩过的一些坑

    SSD是Caffe的一个分支,源码在github上:https://github.com/weiliu89/caffe/tree/ssd $ git clone https://github.com/ ...

  9. 用UGUI制作可根据手指位置自动定位的隐形遥杆

    之前写过遥杆怎么做,这里依然用的是之前的方法,就不介绍了. 之前玩过<蜡烛人>,发现手游版的<蜡烛人>的遥杆是看不见的,手指直接在屏幕左边滑动人物就可以移动,可能是为了增强沉浸 ...

  10. .Net 如何访问主流的各大数据库

    做过开发的都知道,.NET基本可以理解是和MSSQL,windows服务器属于一个好的搭档,正如PHP和MYSQL,LIUNX等也可以理解是一个完美搭配:但是在实际的开发中并不完全是这样的,如果你是学 ...