Goroutine原理介绍 - Go语言中文社区

Goroutine原理介绍


为什么Golang需要单独开发一个Goroutine?

  • 开销问题:

    POSIX的thread API虽然能够提供丰富的API,例如配置自己的CPU亲和性,申请资源等等,线程在得到了很多与进程相同的控制权的同时,开销也非常的大,在Goroutine中则不需这些额外的开销,所以一个Golang的程序中可以支持10w级别的Goroutine。

  • 调度性能:

    在Golang的程序中,操作系统级别的线程调度,通常不会做出合适的调度决策。例如在GC时,内存必须要达到一个一致的状态。在Goroutine机制里,Golang可以控制Goroutine的调度,从而在一个合适的时间进行GC。

Goroutine的实现原理

  • 两种备选方案

    • (M:1)多个用户态的线程对应一个系统线程,它可以做快速的上下文切换。缺点是不能有效利用多核CPU
    • (1:1)一个用户态的线程对应一个系统线程,它可以利用多核机制,但上下文切换需要消耗额外的资源
  • Golang的做法

    • (M:N)Golang采取了一种多对多的方案。M个用户线程对应N个系统线程,缺点增加了调度器的实现难度
  • 角色:

    • M: 代表了系统线程,由操作系统管理

    • G:Goroutine的实体,包括了调用栈,重要的调度信息,例如channel等。

    • P:衔接M和G的调度上下文,它负责将等待执行的G与M对接。

      P的数量由环境变量中的GOMAXPROCS决定,通常来说它是和核心数对应,例如在4Core的服务器上回启动4个线程。G会有很多个,每个P会将Goroutine从一个就绪的队列中做Pop操作,为了减小锁的竞争,通常情况下每个P会负责一个队列。

  • 挂起

    在Goroutine需要执行一个系统调用时,由于M是一个线程,所以必须等待它执行完才能执行其他的Goroutine。当一个新的Goroutine产生,M需要保证会有另外的一个M能够执行这个G,简单来说,当一个M进行系统调用,需要保证有另外的一个M能够继续执行Go代码。

    • 何时恢复?

      当系统调用返回时,M需要找到一个对应的P,以便能够运行Goroutine,它首先会尝试从其他线程中窃取一个P,如果不成功,它会将Goroutine放在一个全局的队列中,并将自己放在thread cache中。

    • 如何窃取

      这里有篇paper来描述这个设计:work-steal.

      简单来说,当队列不平衡时,会从其他队列中截取一部分Goroutine到P上进行调度。

参考链接

https://docs.google.com/document/d/1TTj4T2JO42uD5ID9e89oa0sLKhJYD0Y_kqxDv3I3XMw/edit#
https://morsmachine.dk/go-scheduler
(翻译)[https://www.zhihu.com/question/20862617]

版权声明:本文来源简书,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://www.jianshu.com/p/27c9a0cf4d22
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。
  • 发表于 2020-01-12 12:50:48
  • 阅读 ( 1794 )
  • 分类:Go

0 条评论

请先 登录 后评论

官方社群

GO教程

猜你喜欢