面试题答案
一键面试M:N模型
在Go语言中,Goroutine调度器采用M:N模型。该模型将多个用户级线程(Goroutine)映射到多个内核级线程(操作系统线程)上。这种模型结合了1:1模型(一个用户级线程对应一个内核级线程)和N:1模型(多个用户级线程映射到一个内核级线程)的优点,既能充分利用多核CPU的性能,又能减少线程创建和上下文切换的开销。
G、M、P的含义
- G(Goroutine):代表一个轻量级的用户级线程。它是Go语言并发编程的核心,用户通过
go
关键字创建Goroutine来实现并发任务。Goroutine有自己的栈空间、程序计数器和局部变量等,其创建和销毁的开销远小于操作系统线程。 - M(Machine):代表一个内核级线程,即操作系统线程。M负责执行Goroutine,它与操作系统的线程一一对应。M有自己的栈空间,用于执行Goroutine的代码。
- P(Processor):代表一个逻辑处理器,它包含了运行Goroutine的资源,如Goroutine队列等。P的数量决定了同时可以并发执行的Goroutine数量,默认情况下,P的数量等于CPU的核心数。P为M提供了执行Goroutine的上下文环境。
调度过程中的协作
- 创建与初始化:
- 用户通过
go
关键字创建Goroutine,新创建的Goroutine被放入全局Goroutine队列或者某个P的本地Goroutine队列中。 - 调度器初始化时,会创建一定数量的M和P。M和P在启动时会进行关联。
- 用户通过
- 调度执行:
- M从与之关联的P的本地Goroutine队列中获取Goroutine来执行。如果本地队列为空,M会尝试从全局Goroutine队列或者其他P的本地队列中窃取Goroutine(工作窃取算法)。
- 当一个M执行一个Goroutine时,它会将该Goroutine从队列中取出并执行其代码。在执行过程中,如果Goroutine进行系统调用(如I/O操作),M会将该Goroutine与自己分离,然后M进入休眠状态,等待系统调用完成。此时,P可以继续调度其他M来执行队列中的其他Goroutine。
- 当系统调用完成后,被阻塞的Goroutine会被重新放入队列(通常是全局队列),等待再次被调度执行。
- 动态调整:
- 调度器会根据系统负载和Goroutine的执行情况动态调整M和P的数量。例如,如果有大量的Goroutine需要执行,调度器可能会创建更多的M来提高并发处理能力;如果系统负载较低,调度器可能会减少M的数量以节省资源。