??golang这个新兴的语言,最关键的就在于goroutine,而goroutine的调度又是golang的核心。可以说,没有goroutine,那么这个语言就会毫无意义,没有发明的必要。为了能够更好的写出高质量的代码,最近学习了goroutine的调度,收获良多。写篇文章总结记录一下。
我的参考资料 Analysis of the Go runtime scheduler 一篇分析goroutine的论文,英文的,其实这篇论文写远比很多博客来的通俗易懂,值得一看。
How Goroutines Work这篇博客简略写goroutine和线程的区别,文章底部也有更进一步阅读参考的链接
goroutine与调度器图文并茂的讲了goroutine调度的算法。值得一看。
Go RUNTIME ??Go Runtime 管理着goroutine的调度以及运行时的垃圾回收,而我们今天所讨论主要是goroutine的调度。
??Go程序会被编译成机器码,因为Go提供了,goroutine、channels、垃圾回收,所以就需要一个runtime的框架来支持这些特性。Go 程序可以被看成两层,一层是用户代码,一层是runtime并且是调用一些管理goroutine, channel, 以及一些其他的高层抽象。任何系统调用都要通过runtime层来配合goroutine的调度。下图展示了go程序、Go runtime、以及操作系统之间的关系。
文章图片
??runtime记录着每一个goroutine的状态,并且会在一个线程池上来调度这些goroutine。goroutine是和线程分离的但是却需要依靠线程来运行。所以,有效地在线程池上调度goroutine对go程序的运行效率就显得非常重要了。
线程和goroutine的区别
内存的消耗
??goroutine的创建是不需要的很多内存的,大约2KB栈空间而已,而一个线程一创建就要约1MB内存,一个服务器一个请求创建一个goroutine而没有任何问题,而一个请求创建一个线程则会导致OutOfMemoryError,任何语言用多线程来实现并发都会遇到这个问题。
创建与销毁的代价
??线程的创建与销毁代价是比较大的,所以往往弄一个线程池来解决这个问题。而goroutine的创建销毁都比较cheap, 当然,其实可以看成go已经实现了线程池。
切换代价
??当线程阻塞,其他的线程必须被调度过来被阻塞线程的位置,当线程切换,调度器需要保存所有的寄存器,通用寄存器、程序计数器、栈指针还有其他一些乱七八槽的寄存器,这样的切换代价是很高的,尤其是在频繁进行线程切换的情况下。
??而goroutine的切换代价就很低了,只需要保存3个寄存器:程序计数寄存器、栈指针以及DX。所以,goroutine调度的切换代价以及时间是可以忽略不计的。
goroutine是怎么执行的
??runtime管理调度着所有的goroutine,从出生到死亡。它会分配一些线程,采用多路复用的策略,来执行goroutine。在任何时候,一个线程会执行一个goroutine,如果这个goroutine阻塞了,这个goroutine就会被换出,这个线程就会执行其他的goroutine。因为goroutine是合作式的调度(这里我也不太明白),那么如果一个goroutine不停的循环就会饿死其他的在这个线程上的goroutine。在go1.2中,这个问题已经有所缓解,当进入一个函数后,runtime会不定时的激活调度器,所以一个没有内联函数的循环会被取代。
goroutine阻塞
??goroutine是轻量级的,并且以下原因中不会造成线程阻塞。
1. 网络输入
2. sleeping
3. channel 操作
4. 通过sync包的阻塞操作
【golang|Goroutine的调度分析(一)】即使产生上万的goroutine,如果它们是以上4种原因阻塞的,也不会造成系统资源的浪费,因为runtime会调度其他的goroutine给这些被多路复用的thread执行。
简单来说,goroutine是在线程之上的轻量级抽象,go的使用者不用解决线程的麻烦,而系统也不会感受到goroutine的存在。
线程和处理器
??尽管你不能直接控制runtime创造线程的数量,但是你可以设置系统使用的处理器的核的数量。通过设置GOMAXPROCS,你就可以增加或减少核的数量来改善你程序的性能了。
思考
??和其他语言一样,避免同时操作共享的内存是很重要的,最好的策略是用channel来实现共享,do not cmmunicate by sharing memory; instead, share memory by communicating.
推荐阅读
- 人工智能|干货!人体姿态估计与运动预测
- 分析COMP122 The Caesar Cipher
- 技术|为参加2021年蓝桥杯Java软件开发大学B组细心整理常见基础知识、搜索和常用算法解析例题(持续更新...)
- C语言学习(bit)|16.C语言进阶——深度剖析数据在内存中的存储
- Python机器学习基础与进阶|Python机器学习--集成学习算法--XGBoost算法
- 数据结构与算法|【算法】力扣第 266场周赛
- 数据结构和算法|LeetCode 的正确使用方式
- leetcode|今天开始记录自己的力扣之路
- 人工智能|【机器学习】深度盘点(详细介绍 Python 中的 7 种交叉验证方法!)
- 网络|简单聊聊压缩网络