面试题答案
一键面试Go调度器的M:N模型
- 模型概述:
- 在Go语言的调度器中,M:N模型指的是M个操作系统线程(M个M)对应N个协程(N个G)。这里的M和N是动态变化的,并非固定值。操作系统线程(M)负责实际执行任务,而协程(G)是轻量级的执行单元,由Go语言运行时进行管理。
- 与传统的1:1模型(每个线程对应一个用户级线程)相比,M:N模型允许多个协程复用少量的操作系统线程,从而大大提高了系统资源的利用率。因为创建和销毁操作系统线程的开销较大,而协程的创建和销毁开销极小。
- 调度器组件:
- G(Goroutine):代表一个协程,它包含了协程的栈、程序计数器以及其他与执行相关的信息。
- M(Machine):代表一个操作系统线程,它负责执行G。每个M都有一个本地的G队列,用于存放待执行的G。
- P(Processor):Processor起着桥梁的作用,它将G和M关联起来。每个P都有一个本地的G队列,同时它也维护着一个全局的G队列。P的数量决定了同时能并发执行的G的数量,默认情况下,P的数量等于CPU的核心数。
- 调度流程:
- 当一个G被创建时,它会被放入到某个P的本地G队列中。如果本地G队列满了,G会被放入全局G队列。
- M会从P的本地G队列中获取G并执行。如果本地G队列空了,M会尝试从全局G队列或者其他P的本地G队列中偷取G来执行。
- 当一个G执行阻塞操作(如I/O操作)时,对应的M会阻塞,此时P会将该G从M上摘除,并将其他G调度到该M上执行。当阻塞的G恢复后,它会被重新放入P的本地G队列等待再次执行。
优化协程性能的方法
- 调整调度器参数:
- 设置P的数量:可以通过
runtime.GOMAXPROCS
函数来设置P的数量。例如:
- 设置P的数量:可以通过
package main
import (
"fmt"
"runtime"
)
func main() {
// 设置P的数量为4
runtime.GOMAXPROCS(4)
// 其他代码
}
- 合理设置P的数量可以提高CPU的利用率。如果P的数量设置过小,可能导致CPU资源不能充分利用;如果设置过大,会增加调度开销。一般来说,将P的数量设置为CPU核心数是一个不错的选择。
- 设置环境变量:
- GOMAXPROCS环境变量:除了在代码中使用
runtime.GOMAXPROCS
函数,还可以通过设置GOMAXPROCS
环境变量来控制P的数量。例如,在Linux或macOS系统中,可以在终端中执行以下命令来设置GOMAXPROCS
为4:
- GOMAXPROCS环境变量:除了在代码中使用
export GOMAXPROCS=4
- 在Windows系统中,可以通过以下命令设置:
set GOMAXPROCS=4
- 这样在程序运行时,Go调度器会根据
GOMAXPROCS
的值来确定P的数量,从而优化协程的性能。