Goroutine调度器
Go语言在并发编程有着非常强大的能力,讲到调度器,我们的话题离不开操作系统、进程与线程这些概念,在学习操作系统时,线程是操作系统调度的最基本单元。
在没有学习Go语言之前,线程之间通信,通过内存共享可以实现,但是在调度时每个线程都会占用1M以上内存空间,还有恢复寄存器中内容也需要向操作系统申请或销毁资源,这样会有较大额外开销。
在Go语言中,不需要通过共享内存来通信,而是通过通信来共享内存,这也是Go语言最重要的编程理念。这样的话,Go调度器对Goroutine的上下文切换就减少额外开销了。
Go 调度器概念
M必须拥有P才可以执行G中的代码,P含有一个包含多个G的队列,P可以调度G交由M执行。P的个数在启动时就决定,默认是使用等同CPU的数量,又因为M必须持有一个P才能运行Go代码,所有M个数一般也是等同于CPU个数,以达到尽可能的使用CPU而又不至于产生过多的线程切换开销,这样就大大降低操作系统和硬件的负载。P的个数,可以使用
- G(Goroutine) — 表示 Go协程,它是一个待执行的任务或代表一个Goroutine对象;
- M(Machine) — 表示操作系统的线程,它由操作系统的调度器调度和管理;
- P(Processor) — 表示处理器,它可以被看做运行在线程上的本地调度器;
runtime.GOMAXPROCS()
来设置个数,当在IO密集的场景下可以自行设置来提高性能。Go 语言的调度器是一个非常复杂机制,看了Go源码,虽然有很多注释,但是对于工作经验不足或刚入门Go开发来说,我们还是需要多多的深研究,还有很多细节需要去学习,比如,调度器的设计原理:
- 在早起0.x版本中Go语言调度器是单线程调度器,由G-M模型组成,还有P这个概念;
- 经过不断优化与设计,到1.0版本就引入多线程调度器,但是出现全局锁导致竞争严重;
- 到1.1版本,引入处理器P,然后就构成GMP模型;
- 到Go 1.2 加入抢占式调度器;以及到1.14版本引入基于信号量的抢占式调度器
- Go 中的GMP模型了解吗?
- GMP模型Go有几种状态?(空闲、待运行、运行中、系统调用、等待中、已停止及栈复制中)
- 【Goroutine调度器及面试精选】为什么Go能提高性能,与进程和线程有什么区别?
- 线程和进程会导致CPU额外开销大,切换线程上下文及申请销毁都需要额外开销;
- 线程占用内存一般是固定为2M内存空间;
- Go 栈内存是可变的,初始的时候一般为2KB,最大可扩展为1GB;
- Go 自己实现的调度器,所以创建和销毁开销小;
推荐阅读
- 【golang】leetcode中级-字母异位词分组&无重复字符的最长子串
- 彻底理解Golang Map
- kratos线上开源年会它来啦~
- 深入浅出 Golang 资源嵌入方案(go-bindata篇)
- 深入浅出 Golang 资源嵌入方案(前篇)
- golang 经典案例总结
- Go实战 | 基于有向无环图的并发执行流的实现
- Golang 数组和切片
- Go JSON编码与解码()
- golang map基础知识