前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Golang GMP模型

Golang GMP模型

作者头像
月梦@剑心
发布2023-08-31 10:59:55
3080
发布2023-08-31 10:59:55
举报

Background

早期操作系统是单进程的,只能顺序执行进程,如果进程需要IO,必须要等IO结束才能继续运行,造成了严重的CPU资源的浪费。

为了提升CPU利用率,出现了多进程操作系统,当一个进程被阻塞,可以切换到其他进程运行,大大减少了CPU资源的浪费。

但是进程的切换开销较大,为了更好地实现并发,出现了线程。一个进程可以有很多个线程,他们共享进程的地址空间,切换带来的开销也要比进程切换的开销小。

但是线程切换也要涉及用户态和内核态之间的切换,不够轻量级,于是将线程一分为二,分别是用户线程内核线程。用户线程负责业务上的处理,内核线程负责操作系统层面的处理。

用户线程就称为协程,内核线程还称为线程,协程的调度需要通过协程调度器来实现,协程调度器为内核线程绑定多个协程。

GMP就是Go的goroutine调度模型。

Goroutine内存占用小,一般是几KB,因此可以大量创建;并且可以灵活调度,因为它的切换成本低。

GMP

G. M. P.

  • G代表goroutine。G中存放并发执行的代码入口地址、上下文、运行环境(关联的P和M)、运行栈等执行相关的信息。
  • M是一个内核线程。是操作系统层面调度和执行的实体。
  • P是处理器,是一个抽象的概念,用于处理G,代表M和G所需要的资源。P是一个管理的数据结构,P主要是降低M对G的复杂性,增加一个间接的控制层数据结构。

P持有G的队列,P可以隔离调度,解除P和M的绑定就解除了M对一串G的调用。G并不是执行体,而是存放并发执行体的元信息,包括并发执行的入口函数、堆栈、上下文等信息。为了减少对象的分配和回收,G对象是可以服用,只需要将相关元信息初始化为新值即可。 P的数目默认是CPU核心的数量,M和P的数目差不多,但运行时会根据当前的状态动态地创建M,M有上限值10000;G与P是M:N的关系,M可以成千上万,远远大于N。

线程是运行goroutine的实体,调度器的功能是把可运行的goroutine分配到线程上。

GMP结构

GMP
GMP

图引自Golang深入理解GPM模型

  • 全局队列(Global Queue):存放等待运行的G
  • P的本地队列:存放等待运行的G,但是存储的G数量有限,不超过256个。新建G时,G优先加入到P的本地队列,如果队列满,则会把本地队列中的一半G移动到全局队列。
  • P列表:所有的P都在程序启动时创建,并保存在数组中。P的数量可以通过环境变量$GOMAXPROCS来设置;或者在程序中通过runtime.GOMAXPROCS()来设置。
  • M:线程想要运行任务就得获得P,从P的本地队列中获取G,P队列为空时,M也会尝试从全局队列拿一批G放到本地队列,或者从其他P的本地队列拿一半放到自己的P的本地队列。
  • M列表,当前操作系统分配到当前Go程序的内核线程数,Go语言限定M的最大量是10000
  • M运行G,G执行之后,M会从P获取下一个G,不断重复下去。M与P是1:1的关系。

调度策略

M和P构成一个运行时环境

  • P优先从本地队列中获取goroutine执行
  • 之后从全局队列中获取goroutine执行
  • 再之后去其他的P的本地队列中steal goroutine执行
  • 并不完全按照以上顺序来,会在执行完61个本地goroutine之后,去全局队列尝试拿goroutine执行,避免全局队列中的goroutine饿死
  • 当一个线程因为G进行系统调用阻塞时,线程释放绑定的P,把P转移给其他空闲的线程执行,自己只服务当前这个阻塞了的G。本质是不让CPU闲着,阻塞就切换CPU。当阻塞了的G恢复执行后,在当前线程上执行完之后,还想继续执行,会加入到其他的P队列中,而当前线程会睡眠/销毁。
  • 抢占,一个goroutine最多占用CPU 10ms,防止其他goroutine被饿死。

当执行go func()时发生了什么?

go func()过程
go func()过程

图引自Golang深入理解GPM模型

  1. 创建一个Goroutine
  2. 放入执行go func()的线程对应的P的本地队列中 2.1 如果本地队列已满,则放入全局队列中
  3. M获取G
  4. 调度
  5. 执行,去运行G中的func()函数 5.1 如果执行时,G.func()发生阻塞 5.2 创建一个M或从休眠队列取一个M 5.3 接管当时正在阻塞G的P
  6. 时间片超时,将G重新放回到队列尾部
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2023-05-01,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Background
  • GMP
    • GMP结构
      • 调度策略
      • 当执行go func()时发生了什么?
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档