Goroutine调度器概览
在编程中,"调度"这个概念,我们最熟悉的莫过于操作系统对进程和线程的调度。简单来说,就是操作系统的调度器会按照特定算法,将多个线程合理地分配到各个物理 CPU 核心上执行。
像 Java、C++ 这类传统语言,其并发能力正是构建在这种操作系统线程模型之上的。程序通过调用库函数来创建线程,而线程的调度和管理则完全交由操作系统内核负责。这种方式的弊端在于,内核级线程的创建、销毁以及上下文切换都是重量级操作,需要深入内核态,成本很高,这在一定程度上限制了程序的高并发能力。
为了突破这一瓶颈,Go 语言选择了一条不同的路:它在用户层实现了一种轻量级的线程,称之为 goroutine。每个 goroutine 占用的资源极少,因此在一个 Go 程序中创建成千上万个并发执行的 goroutine 是轻而易举的。
既然 goroutine 是用户级的,操作系统并不可见,那么将它们高效地调度到 CPU 上执行的任务,就不能再由操作系统代劳了。这个重任便落在了 goroutine 调度器的肩上。对于操作系统而言,一个 Go 程序只是一个普通的进程,它只关心这个进程下的线程。因此,如何将数万个 goroutine 高效地映射到少量操作系统线程上执行,并充分发挥多核优势,这一切复杂的调度工作,都需要 Go 自身的调度器独立完成。这正是 Go 高并发能力的核心所在。
Goroutine调度模型简介
Goroutine调度模型(GPM模型)是其高并发能力的基石。通过将轻量级线程Goroutine、逻辑处理器Processor和系统线程M有机结合,使得Go语言在处理高并发场景时表现出色。
GPM模型是Goroutine调度器实现的理论设计,它由三个关键组件组成:
- G- Goroutine:Go语言的轻量级线程
- P- Processor:逻辑处理器,负责调度Goroutine
- M- Machine:系统线程,真正执行代码的实体
Goroutine的GPM模型详解

每个goroutine对应于运行时中的一个抽象结构------G(goroutine),而被视作"物理CPU"的操作系统线程则被抽象为另一个结构------M(machine),从Goroutine(G)的视角看,它所在的P(逻辑处理器)就是其运行时的"CPU",但站在调度器的全局高度,真正的"CPU"是操作系统线程(M)。因此,调度器的关键任务就在于将P与M进行绑定,从而让P本地队列中的G能够通过M真正运行起来。这种P与M动态的、多对多的绑定关系,构成了Go调度器高效并发的基石。
另外一点就是Goroutine的并发模型是建立在操作系统并发能力之上的增强而非替代,Goroutine调度器解决了"海量任务如何在少量线程上高效协作"的问题,操作系统调度器解决"多个线程如何在有限CPU上公平运行"的问题。
Go并发代码学习示例
go
package main
import "time"
func Check1(id int) int {
check1TmCost := 200
time.Sleep(time.Millisecond * time.Duration(check1TmCost))
print("\tgoroutine-", id, ": Check1 ok\n")
return check1TmCost
}
func Check2(id int) int {
check2TmCost := 300
time.Sleep(time.Millisecond * time.Duration(check2TmCost))
print("\tgoroutine-", id, ": Check2 ok\n")
return check2TmCost
}
func Check3(id int) int {
check3TmCost := 400
time.Sleep(time.Millisecond * time.Duration(check3TmCost))
print("\tgoroutine-", id, ": Check3 ok\n")
return check3TmCost
}
func CheckProcess(id int) int {
total := 0
total += Check1(id)
total += Check2(id)
total += Check3(id)
return total
}
func start(id int, f func(int) int, queue <-chan struct{}) <-chan int {
c := make(chan int)
go func() {
total := 0
for {
_, ok := <-queue
if !ok {
c <- total
return
}
total += f(id)
}
}()
return c
}
func max(args ...int) int {
n := 0
for _, v := range args {
if v > n {
n = v
}
}
return n
}
//main函数也是一个gorountine
func main() {
total := 0
num := 11
c := make(chan struct{})
//创建gorountine,go调度器执行调度
c1 := start(1, CheckProcess, c)
c2 := start(2, CheckProcess, c)
c3 := start(3, CheckProcess, c)
for i := 0; i < num; i++ {
//向channel发送信号
c <- struct{}{}
}
//关闭channel
close(c)
total = max(<-c1, <-c2, <-c3)
println("total time cost:", total)
}