MST

星途 面试题库

面试题:Go语言Goroutine调度器中的G - M - P模型深入分析

详细描述Go语言Goroutine调度器的G - M - P模型,包括G、M、P各自的含义与职责,以及它们之间是如何协同工作的。在多核CPU环境下,这个模型是怎样提高调度效率的?
42.1万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试

G - M - P模型概述

  1. G(Goroutine)
    • 含义:代表一个协程,是Go语言中并发执行的最小单位。每个G都有自己的栈空间、程序计数器以及局部变量等。
    • 职责:封装了需要并发执行的任务逻辑,例如一段函数代码。
  2. M(Machine)
    • 含义:代表操作系统线程,它是真正在操作系统层面执行任务的实体。
    • 职责:负责执行Goroutine,M会从P的本地运行队列或者全局运行队列中获取G来执行,并负责与操作系统的交互,如系统调用等。
  3. P(Processor)
    • 含义:处理器,它是G和M之间的中间层,用于管理一组G并为M提供执行上下文。P持有一个本地G的运行队列。
    • 职责:P负责调度G到M上执行,它决定了哪个G能在哪个M上运行。同时,P还管理着一个本地的G队列,当有新的G创建时,优先放入本地队列,当本地队列满了再放入全局队列。

G - M - P协同工作机制

  1. G的创建与调度
    • 当创建一个新的G时,它会首先被放入创建它的P的本地运行队列中。如果P的本地运行队列已满,G会被放入全局运行队列。
    • M会从P的本地运行队列获取G来执行,如果本地队列为空,M会尝试从全局运行队列或者其他P的本地运行队列(通过窃取机制)获取G。
  2. M与P的关联
    • 每个M需要关联一个P才能执行G。在程序启动时,会创建一定数量的P和M,并且将P和M进行绑定。
    • 当M执行系统调用等阻塞操作时,P会与M分离,此时P可以被其他空闲的M获取,继续执行本地队列中的G。
  3. 全局运行队列与本地运行队列交互
    • 全局运行队列存储那些无法放入P本地队列的G。M在本地队列空时,会尝试从全局队列获取G,每次获取一定数量(例如1个)的G到本地队列再执行。同时,当P的本地队列负载过高时,也会将部分G迁移到全局队列。

在多核CPU环境下提高调度效率的方式

  1. 多核并行
    • 每个P可以绑定到不同的CPU核心上,使得多个M可以同时在不同核心上执行不同的G,实现真正的并行计算。例如,一个4核CPU的机器,若有4个P,每个P分别绑定到一个核心,4个M可以同时执行4个不同的G,充分利用多核资源。
  2. 任务窃取
    • 在多核环境下,不同P的本地运行队列负载可能不均衡。当一个M执行完本地队列的G后,会从其他P的本地队列窃取一半的G到自己的本地队列,这种机制使得负载高的P能够将任务分担给负载低的P,提高整体的调度效率。
  3. 减少锁争用
    • 由于每个P有自己的本地运行队列,G的调度和执行在本地队列内进行,减少了对全局资源的竞争,特别是减少了对全局运行队列的锁争用。相比单队列调度模型,在多核环境下,G - M - P模型通过这种方式提高了调度的并发性能。