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