当前位置: 首页 > news >正文

GMP模型入门

go的并发实现采用的是M:N的线程模型,落地就是gmp模型。

M:N模型如下图:

gmp模型如下图:

---

Go 的 GMP 模型是其 高效并发调度机制的核心。GMP 代表:

  • G:Goroutine(用户态线程)

  • M:Machine(绑定内核线程)

  • P:Processor(调度器/执行上下文)

Go 通过这三个组件,实现了 goroutine 的调度和执行,避免了频繁的线程创建与上下文切换,性能优秀。

那么gmp模型是怎么实现的呢?

多个 Goroutine (G) -> 由 P 管理调度 -> 由 M(线程)实际执行;

具体来,需要执行的G是放在P的队列里面等着被执行调用的,不过有时候会有一些岔子,为了保证M的使用率,会有一些具体的调度算法,让G被调来调取,大概情况是:

  1. Hand off:比较重的调度;M阻塞了(syscall),就把M手头的G收走,让其他M去执行;

  2. Work Stealing:M对于的P中的队列没有G了,从其他地方调一些来

  3. 普通调度:G1阻塞了,例如sleep,io了,直接把G1挂起,让其他G被M执行。

  4. 等等

head off可以用图片来理解:

Hand off图源

----


整体的调度思路可以用伪代码来理解:

 // G = Goroutine,代表一个用户级线程(任务)// M = Machine,代表一个工作线程(对应一个内核线程)// P = Processor,代表执行资源(运行队列+执行上下文),M 必须绑定 P 才能运行 G​type G struct {fn func()    // Goroutine 要执行的函数}​type M struct {p *P         // 当前绑定的 Pcurg *G      // 当前正在执行的 G// ... 还有调用栈等}​type P struct {runQueue []*G    // 本地 G 队列// 还包括调度器上下文、调度时间等}​// 系统初始化时,创建 GOMAXPROCS 个 P,通常等于 CPU 核数func initRuntime() {for i := 0; i < GOMAXPROCS; i++ {allP[i] = new(P)}// 启动第一个 MstartM()}​// 启动一个 M(内核线程),从全局找可用的 P,然后调度func startM() {m := new(M)m.p = acquireP()   // 找一个空闲 Pgo m.run()         // 启动内核线程,进入调度循环}​// M 的主循环,持续运行 Gfunc (m *M) run() {for {g := m.p.findRunnableG() // 找到一个可运行的 Gif g == nil {// 若本地队列空了,可以尝试 steal 其他 P 的 Gg = stealFromOtherP()if g == nil {// 若仍找不到,当前 M 休眠stopM(m)return}}m.curg = grunG(g) // 运行 G 的函数m.curg = nil}}​// P 的调度器,从本地 runQueue 中找 goroutinefunc (p *P) findRunnableG() *G {if len(p.runQueue) == 0 {return nil}g := p.runQueue[0]p.runQueue = p.runQueue[1:]return g}​// 当调用 go f() 时,生成一个新的 G,并放入当前 P 的队列func goNew(f func()) {g := &G{fn: f}curP := currentM().pcurP.runQueue = append(curP.runQueue, g)// 若当前 M 忙不过来,可触发 newM 让新线程帮忙跑 G}
 ​

参考资料:

深入浅出 Go 语言 GMP 模型 

刘丹冰 【Golang深入理解GPM模型】https://www.bilibili.com/video/BV19r4y1w7Nx/?share_source=copy_web&vd_source=4ab2dac702abaae48d1782021ca7150c

http://www.xdnf.cn/news/606295.html

相关文章:

  • Lyra学习笔记1地图角色加载流程
  • 树莓派WiringPi库
  • 大模型「瘦身」指南:从LLaMA到MobileBERT的轻量化部署实战
  • php 根据另一个数组中 create_time 的时间顺序,对原始数组进行排序。
  • Neo4j入门第一期(Cypher入门)
  • RabbitMQ ⑥-集群 || Raft || 仲裁队列
  • CentOS 7.6 升级 Openssl 及 Openssh 方法文档
  • Unity EventCenter 消息中心的设计与实现
  • EasyExcel使用
  • GD32 IIC(I2C)通信(使用示例为SD2068)
  • 2.4g芯片引脚功能
  • 56 在standby待机打通uart调试的方法
  • 5.23本日总结
  • SDL2常用函数SDL事件处理:SDL_Event|SDL_PollEvent
  • Vue+css实现扫描动画效果(使用@keyframes scan)
  • RequestBody注解中Map
  • 为什么信号经过线束会有衰减?
  • AG32VH 系列应用指南
  • 嵌入式鸿蒙openharmony应用开发环境搭建与工程创建实现
  • Postgresql 数据库实例管理命令
  • Spring IoC容器初始化过程
  • 设计模式-结构型模式(详解)
  • el-dialog 组件 多层嵌套 被遮罩问题
  • Redis 缓存使用的BigKey问题
  • SAP在金属行业的数字化转型:无锡哲讯科技的智能解决方案
  • A10服务器使用vllm推理框架成功运行Qwen3大模型
  • 机器学习第二十四讲:scikit-learn → 机器学习界的瑞士军刀
  • Rancher 部署与使用指南
  • 使用Rancher在CentOS 环境上部署和管理多Kubernetes集群
  • 如何把一台电脑作为另外一台电脑的显示器