今天小编给大家分享一下Go语言中为什么要有GMP调度模型的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。
GMP调度模型是Go的精髓所在,它合理地解决了多线程并发调度协程的效率问题。
首先得清楚,GMP各代指什么东西。
G: Goroutine的缩写,指协程,运行在线程上。
M: Machine的缩写,即thead,线程,循环的调度协程并执行。
P: Processor的缩写,指处理器,将协程储存到本地队列,并为线程提供未休眠的可用的协程,
线程M各自持有一个处理器P,当要获取协程时优先从P中获取,于是GMP模型图解如下:
大致流程是,线程M从P的队列中获取协程,如果获取不到,就会从全局队列中去竞争锁来获取。
协程G和线程M结构在前几篇已经讲解了,这里解析一下处理器P。
处理器P储存着一批协程,使得线程M可以无锁的从中获取协程,而无需与其他线程去竞争全局队列中的协程,从而提高调度协程效率。
p结构体源码在src\runtime\runtime2.go
中,这里展示部分重要字段。
type p struct {
...
m muintptr // back-link to associated m (nil if idle)
// Queue of runnable goroutines. Accessed without lock.
runqhead uint32
runqtail uint32
runq [256]guintptr
runnext guintptr
...
}
m
为处理器p
所属的线程
runq
是一个储存协程的队列
runqhead
,runqtail
表示队列的头尾指针
runnext
指向下一个可运行的协程
在src\runtime\proc.go
中,有一个schedule
方法,这是线程运行的第一个函数。这函数中,线程需要获取到可运行的协程,代码如下:
func schedule() {
...
// 寻找一个可运行的协程
gp, inheritTime, tryWakeP := findRunnable()
...
}
func findRunnable() (gp *g, inheritTime, tryWakeP bool) {
// 从本地队列中获取协程
if gp, inheritTime := runqget(pp); gp != nil {
return gp, inheritTime, false
}
// 本地队列拿不到则从全局队列中获取协程
if sched.runqsize != 0 {
lock(&sched.lock)
gp := globrunqget(pp, 0)
unlock(&sched.lock)
if gp != nil {
return gp, false, false
}
}
}
从本地队列中获取协程
func runqget(pp *p) (gp *g, inheritTime bool) {
next := pp.runnext // 队列中下一个可运行的协程
if next != 0 && pp.runnext.cas(next, 0) {
return next.ptr(), true
}
...
}
那如果本地队列和全局队列中都没有协程了怎么办呢,难道就让线程这么闲着?
这时候处理器P就会任务窃取,从其他线程的本地队列中窃取一些任务,美其名曰分担其他线程的压力,还提高了自己线程的利用率。
源码在src\runtime\proc.go\stealWork
中,感兴趣可以看看。
新建的协程该分配到本地还是全局队列呢,得分情况:
Go认为新协程的优先级高,于是先寻找本地队列放入,而且还插队。
本队队列满了才放入全局队列。
实际流程为:
随机寻找P
将新协程放入P的runnext
中,意味着下一个就运行该协程,插队了
若P的协程满了,则放入全局队列
源码在src\runtime\proc.go\newproc
函数中。
// Create a new g running fn.
// Put it on the queue of g's waiting to run.
// The compiler turns a go statement into a call to this.
func newproc(fn *funcval) {
gp := getg()
pc := getcallerpc()
systemstack(func() {
newg := newproc1(fn, gp, pc) // 创建新协程
pp := getg().m.p.ptr()
runqput(pp, newg, true) // 寻找本地队列放入
if mainStarted {
wakep()
}
})
}
以上就是“Go语言中为什么要有GMP调度模型”这篇文章的所有内容,感谢各位的阅读!相信大家阅读完这篇文章都有很大的收获,小编每天都会为大家更新不同的知识,如果还想学习更多的知识,请关注亿速云行业资讯频道。
亿速云「云服务器」,即开即用、新一代英特尔至强铂金CPU、三副本存储NVMe SSD云盘,价格低至29元/月。点击查看>>
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。