面试题答案
一键面试调度器工作方式差异
- Goroutine调度器:
- M:N调度模型:Go语言使用M:N调度模型,即多个Goroutine(N个)可以映射到多个操作系统线程(M个)上。Go的调度器在用户态实现,被称为G-M-P模型。其中G代表Goroutine,M代表操作系统线程,P代表处理器(Processor)。P的数量一般等于CPU核心数,它提供了执行Goroutine的资源,M必须关联到一个P才能执行Goroutine。
- 协作式调度:Goroutine采用协作式调度(co - operative scheduling),也叫非抢占式调度。当一个Goroutine执行系统调用(如I/O操作)或主动调用
runtime.Gosched()
时,它会主动让出CPU,调度器会安排其他可运行的Goroutine在该M上执行。
- 传统线程调度器:
- 1:1调度模型:传统线程通常采用1:1调度模型,即一个线程对应一个操作系统线程。操作系统内核负责线程的调度,这是基于抢占式调度(pre - emptive scheduling)。
- 抢占式调度:操作系统内核会根据线程的优先级、时间片等因素,在合适的时机强制暂停当前运行的线程,并切换到其他线程执行。例如,当一个线程的时间片用完,内核会中断该线程,将CPU资源分配给其他线程。
资源占用差异
- Goroutine:
- 轻量级:Goroutine非常轻量级,创建和销毁的开销很小。一个Goroutine初始栈大小通常只有2KB,并且栈空间可以根据需要动态增长和收缩。由于多个Goroutine可以复用少量的操作系统线程,所以在创建大量并发任务时,内存占用和资源开销相对较低。
- 传统线程:
- 重量级:传统线程相对较重,创建和销毁的开销较大。每个线程都需要占用一定的内核资源,包括内核栈(通常为几MB),并且线程上下文切换的开销也比较大。因此,在创建大量线程时,会消耗较多的系统资源,容易导致系统性能下降。