golang的goroutine调度机制

调度器

主要基于三个基本对象上,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的编译工具链还是很强大的,十分有利于程序的优化。

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


作者:正版两只羊
原文:https://blog.csdn.net/liangzhiyang/article/details/52669851

添加小编微信:grey0805,加入知识学习小分队~!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,042评论 6 490
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 89,996评论 2 384
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,674评论 0 345
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,340评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,404评论 5 384
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,749评论 1 289
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,902评论 3 405
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,662评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,110评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,451评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,577评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,258评论 4 328
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,848评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,726评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,952评论 1 264
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,271评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,452评论 2 348

推荐阅读更多精彩内容