MST

星途 面试题库

面试题:Go调度器中Goroutine的生命周期及调度流程

详细描述一个Goroutine从创建到执行完成,在Go调度器中的整个生命周期过程,包括其在不同状态(如创建、就绪、运行、阻塞等)之间如何转换,以及调度器如何处理这些状态变化。
36.7万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试
  1. 创建
    • 当使用 go 关键字创建一个 Goroutine 时,它首先处于 创建 状态。此时,会为该 Goroutine 分配栈空间,初始化其运行时数据结构,如 g 结构体(包含栈指针、程序计数器等信息)。这个 g 结构体是 Goroutine 在运行时的表示。
  2. 就绪
    • 创建完成后,Goroutine 进入 就绪 状态。调度器会将处于就绪状态的 Goroutine 放入本地队列(每个 M 对应一个本地队列,M 是操作系统线程)或者全局队列(当本地队列满时)。本地队列优先被调度器处理,这有助于减少锁的竞争。
  3. 运行
    • 当一个 M(操作系统线程)从本地队列或者全局队列中获取到一个 Goroutine 时,该 Goroutine 进入 运行 状态。M 会将这个 Goroutine 绑定到自身,并开始执行其函数体中的代码。在运行过程中,Goroutine 会使用 M 的栈空间。
    • 调度器采用协作式调度(co - operative scheduling),当 Goroutine 执行到如系统调用、I/O 操作、runtime.Gosched() 等情况时,它会主动让出执行权。
  4. 阻塞
    • 如果 Goroutine 执行系统调用、I/O 操作、等待通道(channel)数据、获取锁等操作时,它会进入 阻塞 状态。例如,当 Goroutine 执行网络 I/O 操作时,它会被阻塞,直到 I/O 操作完成。
    • 处于阻塞状态的 Goroutine 会从 M 上分离,M 可以继续执行其他 Goroutine。阻塞的 Goroutine 会被放入相应的等待队列(如通道等待队列、锁等待队列等)。
  5. 重新就绪
    • 当阻塞的条件解除时,例如 I/O 操作完成、通道有数据可读/可写、获取到锁等,Goroutine 会从等待队列中移除并重新进入 就绪 状态。然后,它会被重新放入调度器的本地队列或全局队列,等待再次被调度执行。
  6. 执行完成
    • Goroutine 执行完其函数体中的所有代码并返回时,它进入 执行完成 状态。此时,调度器会回收该 Goroutine 占用的资源,包括栈空间等。这些资源可以被后续新创建的 Goroutine 复用。

调度器通过维护本地队列、全局队列以及各种等待队列,来管理 Goroutine 在不同状态之间的转换。在多核环境下,调度器会尽量在多个 M 上合理分配 Goroutine 的执行,以充分利用多核资源,提高程序的并发性能。