面试题答案
一键面试M:N调度模型中M和N的含义
- M:代表操作系统线程(Machine)。一个M对应一个操作系统线程,它负责执行实际的任务。M的数量一般受限于系统资源,例如每个操作系统线程都有自己的栈空间等资源消耗。
- N:代表Goroutine(Number of Goroutines)。Goroutine是Go语言中轻量级的用户态线程,N个Goroutine可以被映射到M个操作系统线程上执行 。由于Goroutine非常轻量级,创建和销毁的开销都很小,所以可以在程序中创建大量的Goroutine。
调度器管理和调度Goroutine实现高效并发处理的方式
- Goroutine创建与初始化:当使用
go
关键字创建一个新的Goroutine时,调度器为其分配必要的上下文信息,包括栈空间(初始时通常较小,会随着使用动态增长)、程序计数器(PC)以及用于调度的状态信息等。新创建的Goroutine被放入全局Goroutine队列或者本地Goroutine队列(与某个M关联的队列)中等待执行。 - M与P(Processor)的关系:在Go调度器中,还有一个重要的概念是P(Processor),它代表逻辑处理器。每个M需要绑定到一个P才能运行Goroutine。P的数量可以通过
runtime.GOMAXPROCS
函数设置,默认值是机器的CPU核心数。P维护着本地Goroutine队列,并且负责执行调度循环,从本地或全局队列中选择Goroutine并交给绑定的M执行。 - 调度策略:
- 本地队列优先:M在运行时,首先会尝试从与它绑定的P的本地Goroutine队列中获取一个Goroutine来执行。如果本地队列为空,M会尝试从全局Goroutine队列中偷取一部分Goroutine到本地队列。
- 协作式调度:Go的调度器采用协作式调度(Cooperative Scheduling)策略。当一个Goroutine执行系统调用(如I/O操作)、调用
runtime.Gosched()
函数主动让出CPU或者进行垃圾回收(GC)相关操作时,它会主动暂停自己,将执行权交回给调度器。调度器则可以安排其他等待的Goroutine在这个M上运行。 - 全局队列平衡:为了保证全局队列中Goroutine的公平调度,当M从全局队列获取Goroutine时,它会按照一定的规则批量获取,以确保所有的Goroutine都有机会被执行。同时,各个P之间也会定期检查全局队列,防止任务堆积。
- M的管理:调度器会根据系统负载情况动态调整M的数量。当有大量Goroutine需要执行且当前活跃的M数量不足以处理时,调度器会创建新的M来分担工作。而当一些M处于空闲状态,且系统负载较低时,调度器会将这些空闲的M销毁以节省资源。
通过上述机制,Go语言的Goroutine调度器能够高效地管理和调度大量的Goroutine,充分利用系统资源,实现高效的并发处理。