GMP理解
创始人
2025-05-31 14:15:45
0

目录

  • goroutine
  • 动态栈
  • goroutine调度
  • GOMAXPROCS
  • 为什么不直接将本地队列放在 M 上、而是要放在 P 上呢?
  • 参考文档(来源)

goroutine

Goroutine 是 Go 语言支持并发的核心,在一个Go程序中同时创建成百上千个goroutine是非常普遍的,一个goroutine会以一个很小的栈开始其生命周期,一般只需要2KB。区别于操作系统线程由系统内核进行调度, goroutine 是由Go运行时(runtime)负责调度。例如Go运行时会智能地将 m个goroutine 合理地分配给n个操作系统线程,实现类似m:n的调度机制,不再需要Go开发者自行在代码层面维护一个线程池。

Goroutine 是 Go 程序中最基本的并发执行单元。每一个 Go 程序都至少包含一个 goroutine——main goroutine,当 Go 程序启动时它会自动创建。

动态栈

操作系统的线程一般都有固定的栈内存(通常为2MB),而 Go 语言中的 goroutine 非常轻量级,一个 goroutine 的初始栈空间很小(一般为2KB),所以在 Go 语言中一次创建数万个 goroutine 也是可能的。并且 goroutine 的栈不是固定的,可以根据需要动态地增大或缩小, Go 的 runtime 会自动为 goroutine 分配合适的栈空间。

goroutine调度

操作系统内核在调度时会挂起当前正在执行的线程并将寄存器中的内容保存到内存中,然后选出接下来要执行的线程并从内存中恢复该线程的寄存器信息,然后恢复执行该线程的现场并开始执行线程。从一个线程切换到另一个线程需要完整的上下文切换。因为可能需要多次内存访问,索引这个切换上下文的操作开销较大,会增加运行的cpu周期。

区别于操作系统内核调度操作系统线程,goroutine 的调度是Go语言运行时(runtime)层面的实现,是完全由 Go 语言本身实现的一套调度系统——go scheduler。它的作用是按照一定的规则将所有的 goroutine 调度到操作系统线程上执行。

在经历数个版本的迭代之后,目前 Go 语言的调度器采用的是 GPM 调度模型。

在这里插入图片描述

其中:

  • G:表示 goroutine,每执行一次go f()就创建一个 G,包含要执行的函数和上下文信息。

  • 全局队列(Global Queue):存放等待运行的 G。

  • P:表示 goroutine 执行所需的资源,最多有 GOMAXPROCS 个。Processor的缩写,代表一个虚拟的处理器,它维护一个局部的可运行的 G 队列,可以通过 CAS 的方式无锁访问,工作线程 M 优先使用自己的局部运行队列中的 G,只有必要时才会去访问全局运行队列,这大大减少了锁冲突,提高了大量 G 的并发性。每个 G 要想真正运行起来,首先需要被分配一个 P。

  • P 的本地队列:同全局队列类似,存放的也是等待运行的G,存的数量有限,不超过256个。新建 G 时,G 优先加入到 P 的本地队列,如果本地队列满了会批量移动部分 G 到全局队列。

  • M:Machine的缩写,代表了内核线程 OS Thread,CPU调度的基本单元;线程想运行任务就得获取 P,从 P 的本地队列获取 G,当 P 的本地队列为空时,M 也会尝试从全局队列或其他 P 的本地队列获取 G。M 运行 G,G 执行之后,M 会从 P 获取下一个 G,不断重复下去。

Goroutine 调度器和操作系统调度器是通过 M 结合起来的,每个 M 都代表了1个内核线程,操作系统调度器负责把内核线程分配到 CPU 的核上执行。

单从线程调度讲,Go语言相比起其他语言的优势在于OS线程是由OS内核来调度的, goroutine 则是由Go运行时(runtime)自己的调度器调度的,完全是在用户态下完成的, 不涉及内核态与用户态之间的频繁切换,包括内存的分配与释放,都是在用户态维护着一块大的内存池, 不直接调用系统的malloc函数(除非内存池需要改变),成本比调度OS线程低很多。 另一方面充分利用了多核的硬件资源,近似的把若干goroutine均分在物理线程上, 再加上本身 goroutine 的超轻量级,以上种种特性保证了 goroutine 调度方面的性能。

GOMAXPROCS

  1. Go运行时的调度器使用GOMAXPROCS参数来确定需要使用多少个 OS 线程来同时执行 Go 代码。默认值是机器上的 CPU 核心数。例如在一个 8 核心的机器上,GOMAXPROCS 默认为 8。

  2. Go语言中可以通过runtime.GOMAXPROCS函数设置当前程序并发时占用的 CPU逻辑核心数。(Go1.5版本之前,默认使用的是单核心执行。Go1.5 版本之后,默认使用全部的CPU 逻辑核心数。)

为什么不直接将本地队列放在 M 上、而是要放在 P 上呢?

当一个线程 M 阻塞(可能执行系统调用或 IO请求)的时候,可以将和它绑定的 P 上的 G 转移到其他线程 M 去执行,如果直接把可运行 G 组成的本地队列绑定到 M,则万一当前 M 阻塞,它拥有的 G 就不能给到其他 M 去执行了。

参考文档(来源)

  1. 深入分析Go1.18 GMP调度器底层原理.腾讯技术工程

相关内容

热门资讯

中央定调“能源强国”,核电板块... 12月15日,A股核电概念再度上涨,成分股天力复合(920576)上涨13.58%,雪人集团(002...
募资约61亿!深蓝汽车拟增资扩... 12月13日,长安汽车发布公告称,为满足业务发展和资金需求,长安汽车之控股子公司深蓝汽车科技有限公司...
优必选、越疆机器人入选“港交所... 新京报贝壳财经讯(记者韦博雅)12月14日和15日,优必选、越疆机器人分别宣布入选香港交易所推出的“...
一天三套豪宅!中国炒房客“带飞... 越南楼市,彻底疯了!最近,胡志明市一个楼盘开盘的清晨,仿佛让人看到了2015年上海内环楼市开盘现场的...
又要诞生一个“上纬新材”? 这世上本没有路,走的人多了,便就成了路——这个道理在资本市场上也说得通。继智元机器人入主上纬新材和中...
破局“内卷”开拓蓝海——企业界... 文|《中国企业家》记者 马吉英见习记者张昊编辑|李会头图来源|视觉中国“低价低质的恶性竞争,正在卷死...
中源家居上演“天地板” 新京报贝壳财经讯 12月15日,中源家居盘中一度跌停,上演“天地板”,成交额超5.9亿元。中源家居此...
罗永浩炮轰千元酒店祸害不明真相... 01.豆包手机助手回应「能截屏银行安全键盘」02.Reddit起诉澳大利亚青少年“社媒禁令”03.O...
新农合涨到400元,农民断缴背... 解决新农合断缴问题的根本,不在于村干部的催缴手段有多硬,而在于制度设计上,是否足够人性化,是否真正回...
陈天桥最新撰文:系统的融化,从... 我们在旧结构上越是用力地“加AI”,就越有可能是在给那些本该被淘汰的系统续命。文|陈天桥盛大集团创始...