为什么80%的码农都做不了架构师?>>>   

著作权归作者所有。
商业转载请联系作者获得授权,非商业转载请注明出处。
作者:Yi Wang
链接:http://www.zhihu.com/question/20862617/answer/27964865
来源:知乎

Go runtime的调度器:
在了解Go的运行时的scheduler之前,需要先了解为什么需要它,因为我们可能会想,OS内核不是已经有一个线程scheduler了嘛?
熟悉POSIX API的人都知道,POSIX的方案在很大程度上是对Unix process进场模型的一个逻辑描述和扩展,两者有很多相似的地方。 Thread有自己的信号掩码,CPU affinity等。但是很多特征对于Go程序来说都是累赘。 尤其是context上下文切换的耗时。另一个原因是Go的垃圾回收需要所有的goroutine停止,使得内存在一个一致的状态。垃圾回收的时间点是不确定的,如果依靠OS自身的scheduler来调度,那么会有大量的线程需要停止工作。

单独的开发一个GO得调度器,可以是其知道在什么时候内存状态是一致的,也就是说,当开始垃圾回收时,运行时只需要为当时正在CPU核上运行的那个线程等待即可,而不是等待所有的线程。

用户空间线程和内核空间线程之间的映射关系有:N:1,1:1和M:N
N:1是说,多个(N)用户线程始终在一个内核线程上跑,context上下文切换确实很快,但是无法真正的利用多核。
1:1是说,一个用户线程就只在一个内核线程上跑,这时可以利用多核,但是上下文switch很慢。
M:N是说, 多个goroutine在多个内核线程上跑,这个看似可以集齐上面两者的优势,但是无疑增加了调度的难度。
<img src="https://pic1.zhimg.com/2f5c6ef32827fb4fc63c60f4f5314610_b.jpg" data-rawwidth="391" data-rawheight="103" class="content_image" width="391">
Go的调度器内部有三个重要的结构:M,P,S
M:代表真正的内核OS线程,和POSIX里的thread差不多,真正干活的人
G:代表一个goroutine,它有自己的栈,instruction pointer和其他信息(正在等待的channel等等),用于调度。
P:代表调度的上下文,可以把它看做一个局部的调度器,使go代码在一个线程上跑,它是实现从N:1到N:M映射的关键。
<img src="https://pic1.zhimg.com/67f09d490f69eec14c1824d939938e14_b.jpg" data-rawwidth="400" data-rawheight="391" class="content_image" width="400">
图中看,有2个物理线程M,每一个M都拥有一个context(P),每一个也都有一个正在运行的goroutine。
P的数量可以通过GOMAXPROCS()来设置,它其实也就代表了真正的并发度,即有多少个goroutine可以同时运行。
图中灰色的那些goroutine并没有运行,而是出于ready的就绪态,正在等待被调度。P维护着这个队列(称之为runqueue),
Go语言里,启动一个goroutine很容易:go function 就行,所以每有一个go语句被执行,runqueue队列就在其末尾加入一个
goroutine,在下一个调度点,就从runqueue中取出(如何决定取哪个goroutine?)一个goroutine执行。

为何要维护多个上下文P?因为当一个OS线程被阻塞时,P可以转而投奔另一个OS线程!
图中看到,当一个OS线程M0陷入阻塞时,P转而在OS线程M1上运行。调度器保证有足够的线程来运行所以的context P。
<img src="https://pic3.zhimg.com/f1125f3027ebb2bd5183cf8c9ce4b3f2_b.jpg" data-rawwidth="550" data-rawheight="400" class="origin_image zh-lightbox-thumb" width="550" data-original="https://pic3.zhimg.com/f1125f3027ebb2bd5183cf8c9ce4b3f2_r.jpg">
图中的M1可能是被创建,或者从线程缓存中取出。

当MO返回时,它必须尝试取得一个context P来运行goroutine,一般情况下,它会从其他的OS线程那里steal偷一个context过来,
如果没有偷到的话,它就把goroutine放在一个global runqueue里,然后自己就去睡大觉了(放入线程缓存里)。Contexts们也会周期性的检查global runqueue,否则global runqueue上的goroutine永远无法执行。
<img src="https://pic2.zhimg.com/31f04bb69d72b72777568063742741cd_b.jpg" data-rawwidth="550" data-rawheight="400" class="origin_image zh-lightbox-thumb" width="550" data-original="https://pic2.zhimg.com/31f04bb69d72b72777568063742741cd_r.jpg">
另一种情况是P所分配的任务G很快就执行完了(分配不均),这就导致了一个上下文P闲着没事儿干而系统却任然忙碌。但是如果global runqueue没有任务G了,那么P就不得不从其他的上下文P那里拿一些G来执行。一般来说,如果上下文P从其他的上下文P那里要偷一个任务的话,一般就‘偷’run queue的一半,这就确保了每个OS线程都能充分的使用。

转载于:https://my.oschina.net/fdhay/blog/617349

Go runtime的调度器相关推荐

  1. 【Linux 内核】CFS 调度器 ① ( CFS 完全公平调度器概念 | CFS 调度器虚拟时钟 Virtual Runtime 概念 | 四种进程优先级 | 五种调度类 )

    文章目录 一.CFS 调度器概念 ( 完全公平调度器 ) 二.CFS 调度器虚拟时钟概念 ( Virtual Runtime ) 三.进程优先级 ( 调度优先级 | 静态优先级 | 正常优先级 | 实 ...

  2. Linux进程调度-deadline调度器

    Linux内核中定义了5个调度器类,分别对应5个调度器,调度优先级顺序由高到低依次为:stop_sched_class.dl_sched_class.rt_sched_class.fair_sched ...

  3. golang源码分析:调度器chan调度

    golang调度机制chan调度 golang的调度策略中,碰见阻塞chan就会将该chan放入到阻塞的g中,然后再等待该chan被唤醒,这是golang调度器策略的主动调度策略之一,其中还有其他的主 ...

  4. c++ 初始化 代码 应放在那里_Go语言goroutine调度器初始化 (12)

    先吐槽一下,知乎编辑器居然不支持汇编语言,代码的空格也给我弄没了,你说你把运算符两边的空格搞掉就搞掉吧,还能看,你为啥要把if, for后面的空格也搞掉啊... 本文是<Go语言调度器源代码情景 ...

  5. 返璞归真的Linux BFS调度器

    自Linux 2.6以来(严格说应该是2.5),O(n)调度器被人们认为是一种千年之前就应该抛弃的东西被重重的甩开了,此后出现了O(1),CFS等,再也没人提起O(n)了.说实话,Linux的调度器远 ...

  6. go 获取内核个数_图解Go运行时调度器

    多goroutines形式的Go并发是编写现代并发软件的一种非常方便的方法,但是您的Go程序是如何高效地运行这些goroutines的呢? 在这篇文章中,我们将深入Go运行时底层,从设计角度了解Go运 ...

  7. 7种主流案例,告诉你调度器架构设计通用法则(干货!)

    女主宣言 今天小编为大家转载一篇来自DBAplus社群的干货文章,希望能够帮助大家对关于调度器的理解.作者张晨,Strikingly数据平台工程师,算法.分布式系统和函数式编程爱好者.Shanghai ...

  8. 并发问题的解决思路以及Go语言调度器工作原理

    上周的文章<Go并发编程里的数据竞争以及解决之道>最后留下了一个用并发解决的思考题,期间有几位同学留言说了自己的实现思路,也有两位直接私信发代码让我看的,非常感谢几位的积极参与.今天的文章 ...

  9. 上周并发题的解题思路以及介绍Go语言调度器

    上周的文章<Go并发编程里的数据竞争以及解决之道>最后留下了一个用并发解决的思考题,期间有几位同学留言说了自己的实现思路,也有两位直接私信发代码让我看的,非常感谢几位的积极参与.今天的文章 ...

最新文章

  1. qt 控件 背景色 透明 除去边框
  2. SAP MM PR单据类型的配置里‘Control’和’Doc.Type’字段的作用?
  3. 一款遥控器拆解之后可利用的元器件
  4. 菜鸟学习计划浅谈之Linux系统
  5. 设计模式--简单工厂VS工厂VS抽象工厂
  6. c语言形参的隐含存储类型,C存储类型
  7. 禁止ping入自己的主机
  8. 暴雪还不赶快?劳拉与光之守护者PC平台登陆
  9. Android—RxJava库知识
  10. 我们怎样确保从大数据计算中获得价值
  11. 最长回文子序列与最长回文子串
  12. 免费下载卫星地图 高清卫星地图软件
  13. 扩展城市信道etu模型matlab仿真,信道估计
  14. 我爱淘冲刺阶段站立会议2每天任务5
  15. 终端数据防泄漏案例分析
  16. Redis技术架构演进
  17. 2DPCA人脸识别--python
  18. 论文精读:GHM:Gradient Harmonized Single-stage Detector
  19. 使用计算机打印文字,Word打印出的文字与电脑上的显示不同怎么办
  20. munmap_chunk(): invalid pointer:

热门文章

  1. 分站实现php,php城市分站是什么原理
  2. java basic认证_Basic认证
  3. php exchange,PHP SDK for digital currency exchange
  4. 第一周冲刺_周三总结
  5. Error:Execution failed for task ':app:clean'.
  6. Docker: Failed to get D-Bus connection: No connection to service
  7. 【python自动化第八篇:网络编程】
  8. phpstorm version 2016.2 License Server激活
  9. C++ stringstream的用法
  10. 使用hibernate建立mysql连接以及生成映射类和配置文件*.cfg.xml