简析goroutine调度原理

相较于操作系统线程,goroutine的资源占用和使用代价要小得多。可以创建成百上万甚至上千万个goroutine。Go运行时负责对goroutine进行调度惯例,即决定何时哪个goroutine获得资源执行,哪个goroutine停止执行让出资源,哪个goroutine被唤醒恢复执行等。

goroutine是Go语言并发的基础,也是最基本的执行单元。Go基于goroutine建立了GPM调度模型。

G:goroutine在go运行时中的抽象模型。代表goroutine,存储了goroutine的执行栈信息、goroutine状态及goroutine函数信息,G对象是可以重复使用的。

P:G和M之间的一个中间层。P的数量决定了系统内最大可并行的G数量(P数量 < CPU核数)

M: 对操作系统线程(被视为”物理CPU“)的一种抽象,代表这真正的计算资源

G想要运行起来,首先需要分配一个P,即进入P的本地运行队列中(local runq),P又和M绑定,P和M绑定后P的本地队列中的G才能运行起来。P和M的关系类似Linux操作系统调度层面用户线程与内核线程的对应关系:多对多。

Go调度器实现了抢占式调度,调度到死循环后不会一直执行下去,即G不会一直占用分配给它的P和M,位于P的本地队列中的其他G可以得到调度。

import (
	"fmt"
	"time"
)

func endlessLoop1() {
	for {
		time.Sleep(1 * time.Second)
		fmt.Println("I am endlessLoop1")
	}
}

func endlessLoop2() {
	for {
		time.Sleep(1 * time.Second)
		fmt.Println("I am endlessLoop2")
	}
}

func main() {
    runtime.GOMAXPROCS(1)  // 设置P的数量,此处为1,即多个goroutine都加入一个P的本地队列中等待被调度
	go endlessLoop1()
	go endlessLoop2()

	for {
		time.Sleep(1 * time.Second)
		fmt.Println("I can be arrived!")
	}
}

上边代码中启动了三个goroutine:一个main goroutine,其中for循环为死循环。两个endlessLoop goroutine,endlessLoop也是一个死循环。下面运行程序

go run go-preempt-scheduler.go
I can be arrived!
I am endlessLoop1
I am endlessLoop2
I can be arrived!
I am endlessLoop1
...

从运行结果可以看到,三个在一个P的队列中的死循环goroutine都得到了调度。

Go语言基础及实战 文章被收录于专栏

Go语言学习笔记、语法知识、技术要点和个人理解及实战

全部评论
大佬nb
点赞 回复 分享
发布于 2023-04-09 23:15 福建

相关推荐

来个大佬救一下,为上投了都是石沉大海了,没实习经历的话怕秋招直接进不了面。什么实习这么难找,基本
心态爆炸了:现在正式的岗位都少,实习基本不咋招的,除了大厂,中小企业其实没那么多岗位需求,就算是有,大多都是招一两个廉价劳动力,同时,他们也会希望你一来就能干活的,没时间培训你,就让你了解公司的项目,你了解完就可以开始干活。再者是,很多低质量的实习其实用处没有那么大的。我去年也是找实习找到破防,最后去了一家深圳的小公司实习,工作对我来说很简单,甚至不如我在学校做的项目,秋招的时候,这段实习经历也并没有帮上什么忙,投递简历,依旧非常低的回复率。低回复率是常态,尤其是找实习,找不到,那就把重心放在优化自己的简历和项目,多看八股文,锻炼自己的面试能力,多看别人的面经,自己模拟面试,等秋招的时候,只要有那么寥寥几次,好好抓住那几次机会。
点赞 评论 收藏
分享
机械打工仔:有说的你怀疑一下就行了,直接问也太实诚了
点赞 评论 收藏
分享
不愿透露姓名的神秘牛友
07-03 14:32
点赞 评论 收藏
分享
评论
1
1
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务