面试题答案
一键面试Go 语言中 goroutine 调度器的工作原理
- M:N 调度模型:Go 语言的调度器采用 M:N 调度模型,即多个 goroutine(N)映射到多个操作系统线程(M)上。这种模型可以有效利用多核 CPU 资源,并且在 goroutine 阻塞时不会阻塞操作系统线程。
- G-M-P 架构
- G(Goroutine):表示一个 goroutine,它包含了 goroutine 的栈、程序计数器以及其他需要的执行状态。
- M(Machine):代表一个操作系统线程,它负责执行 goroutine。每个 M 都有一个指向 P 的指针。
- P(Processor):处理器,它包含了一个本地的 goroutine 队列,并且负责在 M 上调度 goroutine。P 的数量默认等于 CPU 的核心数,可以通过
runtime.GOMAXPROCS
函数进行调整。
- 调度流程
- 初始化:程序启动时,创建一定数量的 M 和 P。每个 P 初始化自己的本地 goroutine 队列。
- 创建 goroutine:当使用
go
关键字创建一个新的 goroutine 时,它会被放入某个 P 的本地队列或者全局队列中。 - 调度执行:M 从 P 的本地队列获取 goroutine 来执行。如果本地队列空了,M 会尝试从全局队列或者其他 P 的本地队列偷取 goroutine 来执行。
- 阻塞处理:当一个 goroutine 发生阻塞(例如进行系统调用、channel 操作等),对应的 M 会阻塞,而 P 会将这个 goroutine 从 M 上摘除,并放入阻塞队列。同时,P 会寻找其他可运行的 goroutine 并分配给另一个空闲的 M 执行。当阻塞的 goroutine 恢复可运行状态时,它会被重新放入某个 P 的本地队列。
goroutine 在实际项目中的常见使用场景
- 并发网络请求:在处理多个网络请求时,每个请求可以用一个 goroutine 来执行,这样可以并行处理多个请求,提高效率。例如在一个爬虫项目中,同时抓取多个网页的数据。
- 任务队列处理:可以将任务放入一个 channel 中作为任务队列,然后启动多个 goroutine 从队列中取出任务并执行。比如在一个图像处理系统中,将图片处理任务放入队列,由多个 goroutine 并行处理。
- 实时数据处理:在实时系统中,如物联网数据采集和处理,使用 goroutine 可以实时接收和处理来自多个传感器的数据。每个传感器的数据接收和处理可以用一个单独的 goroutine 来实现。
- 分布式系统通信:在分布式系统中,节点之间的通信可以使用 goroutine 来处理。例如,一个节点接收来自其他节点的消息并进行相应处理,每个消息处理可以用一个 goroutine 来完成,以实现高效的并发处理。