golang的goroutine调度机制

版权声明:本文为博主原创文章,未经博主允许不得转载。

 
 

一直对goroutine的调度机制很好奇,最近在看雨痕的golang源码分析,(基于go1.4)

感觉豁然开朗,受益匪浅;

去繁就简,再加上自己的一些理解,整理了一下

~~

调度器

主要基于三个基本对象上,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,其过程:

1.     先找到一个空闲的P,如果没有则直接返回,(哈哈,这个地方就保证了进程不会占用超过自己设定的cpu个数)

2.     调用系统api创建线程,不同的操作系统,调用不一样,其实就是和c语言创建过程是一致的,(windows用的是CreateThread,linux用的是clone系统调用),(*^__^*)嘻嘻……

3.     然后创建的这个线程里面才是真正做事的,循环执行G任务

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

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

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

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

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

4.     O(∩_∩)O哈哈~,如果没有遇到非内联函数(有时候正常的小函数会被优化成内联函数)调用的话,那就惨了,会一直执行这个G任务,直到它自己结束;如果是个死循环,并且GOMAXPROCS=1的话,恭喜你,夯住了!亲测,的确如此

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

1.     中断的时候将寄存器里的栈信息,保存到自己的G对象里面

2.     当再次轮到自己执行时,将自己保存的栈信息复制到寄存器里面,这样就接着上次之后运行了。 ~\(≧▽≦)/~

但是还有一个问题,就是系统启动的过程,雨痕没有说的太明白,我一直有很多问题都狠疑惑(第一个M怎么来的?,G怎么找到对应的P?等等),这个让我蛋疼了好久~

不过我自己意淫了一下,补充在下面,欢迎大家指正

1.     系统启动的时候,首先跑的是主线程,那第一个M应该就是主线程吧(按照C语言的理解,嘿嘿),这里叫M1,可以看前面的图

2.     然后这个主线程会绑定第一个P1

3.     咱们写的main函数,其实是作为一个goroutine来执行的(雨痕说的)

4.     也就是第一个P1就有了一个G1任务,然后第一个M1就执行这个G1任务(也就是main函数),创建这个G1的时候不用创建M了,因为已经有了M1

5.     这个main函数里面所有的goroutine,都绑定到当前的M1所对应的P1上,O(∩_∩)O哈哈~

6.     然后创建main里的goroutine的时候(比如G2),就会创建新的M2,新的M2里的初始P2的本地任务队列是空的,会从P1里面取一些过来,哈哈

7.     这样两个M1,M2各自执行自己的G任务,再依次往复,这下就圆满了~~~

综上:

所以goroutine是按照抢占式调度的,一个goroutine最多执行10ms就会换作下一个

这个和目前主流系统的的cpu调度类似(按照时间分片)

windows:20ms

linux:5ms-800ms

到这里都差不多了,这些在雨痕的笔记里面都有更详细的描述,不过很多地方比较凌乱,比较复杂,这里筛检了很多,方便读者理解

注意:

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

如果有任何疑问,欢迎提出,

随时更新

(这篇文章是去年整理的,记录公司内部wiki上~)

[转]golang的goroutine调度机制的更多相关文章

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

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

  2. [golang学习] goroutine调度

    这两天有些闲功夫, 学习下golang, 确实非常简洁. 不过有些缺憾. 在我的测试中. golang的调度(goroutine)似乎不是非常好. func say(k int) { fmt.Prin ...

  3. go语言之行--golang核武器goroutine调度原理、channel详解

    一.goroutine简介 goroutine是go语言中最为NB的设计,也是其魅力所在,goroutine的本质是协程,是实现并行计算的核心.goroutine使用方式非常的简单,只需使用go关键字 ...

  4. golang GMP goroutine调度器

    Goroutine可以动态的伸缩栈的大小,最小2-4kb,最大1GB

  5. 谈谈Golang中goroutine的调度问题

    goroutine的调度问题,同样也是我之前面试的问题,不过这个问题我当时并不是很清楚,回来以后立马查阅资料,现整理出来备忘. 有一些预备知识需要说明,就是操作系统中的线程.操作系统中的线程分为两种: ...

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

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

  7. golang核心Goroutine和channel

    一.Goroutine 1.介绍 goroutine简介 goroutine是go语言中最为NB的设计,也是其魅力所在,goroutine的本质是协程,是实现并行计算的核心.goroutine使用方式 ...

  8. 弄懂goroutine调度原理

    goroutine简介 golang语言作者Rob Pike说,"Goroutine是一个与其他goroutines 并发运行在同一地址空间的Go函数或方法.一个运行的程序由一个或更多个go ...

  9. Go语言goroutine调度器概述(11)

    本文是<go调度器源代码情景分析>系列的第11篇,也是第二章的第1小节. goroutine简介 goroutine是Go语言实现的用户态线程,主要用来解决操作系统线程太“重”的问题,所谓 ...

随机推荐

  1. 【转】Angular学习总结--很详细的教程

    *这篇文章是转来的,做了自己的一点修改,排版.原始出处不明,如涉及原博主版权问题,请及时告知,我将会立即删除*. 1 前言 前端技术的发展是如此之快,各种优秀技术.优秀框架的出现简直让人目不暇接,紧跟 ...

  2. [Windows端口占用] 找到占用端口的进程并杀死

    命令行: netstat -aon|findstr "80" 会得到类似下列的数据 TCP 0.0.0.0:80 0.0.0.0:0 LISTENING 2736 2736代表占用 ...

  3. MapReduce的倒排索引

    MapReduce的倒排索引 索引: 什么是索引:索引(Index)是帮助数据库高效获取数据的数据结构.索引是在基于数据库表创建的,它包含一个表中某些列的值以及记录对应的地址,并且把这些值存储在一个数 ...

  4. 2015-10-05 js3

    Javascript 实例2九九乘法表 var s = ""; s += "<table>"; for (var i = 1; i < 10; ...

  5. analyse web.xml of hello1

    web.xml注释分析: 补充: 一.XML文档的xmlns.xmlns:xsi和xsi:schemaLocation (参考博客:https://www.cnblogs.com/osttwz/p/6 ...

  6. 【log4j】使用注意事项

    实际过程中,使用log4j遇到的一些问题,进行总结: 1.log4j.properties文件的放置路径: 必须放在src的根目录下,这样就不需要额外的加载了 2.申明一个log对象 Logger l ...

  7. 2018,ANG发展峰会惊喜来袭

    区块链的分享——ANG发展峰会重大来袭!2018年10月28日 时至今日,区块链技术已经被越来越多的人所熟知.法国著名的高新公司——凡赛公司汇集世界区块链专家,斥巨资打造了行业领先的区块链技术团队,并 ...

  8. spoj mgame

    题解: f[i][j]表示先后手最大差 g[i][j]表示在最大差的时候是否有后手没得走 代码: #include<bits/stdc++.h> using namespace std; ...

  9. vuex-Module

    Vuex 允许将 store 分割成模块(module). 每个模块拥有自己的 state.mutation.action.getter.甚至是嵌套子模块 const moduleA = { stat ...

  10. SQL-47 如何获取emp_v和employees有相同的数据?

    题目描述 存在如下的视图:create view emp_v as select * from employees where emp_no >10005;如何获取emp_v和employees ...