MST

星途 面试题库

面试题:Go调度器的调度策略

Go调度器采用了哪些调度策略来管理Goroutine的执行?这些策略在不同场景下是如何协同工作以提高程序的整体性能的?请举例说明。
38.6万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试

Go调度器采用的调度策略

  1. M:N调度模型
    • Go调度器采用M:N调度模型,即多个用户级线程(Goroutine)映射到多个内核级线程(M)上。这使得Go可以在少量的内核线程上高效运行大量的Goroutine,避免了传统1:1模型中创建大量内核线程带来的资源开销。
  2. G - P - M模型
    • G(Goroutine):代表用户级的协程,是Go语言中轻量级的执行单元。
    • P(Processor):处理器,它包含了运行Goroutine的资源,如Goroutine队列。每个P都有一个本地的Goroutine队列,同时也可以从全局Goroutine队列中获取Goroutine。P的数量一般由GOMAXPROCS环境变量决定,默认等于CPU核心数。
    • M(Machine):内核级线程,负责执行Goroutine。M需要绑定到一个P上才能运行Goroutine。
  3. 抢占式调度
    • Go 1.14版本引入了协作式抢占的增强版——抢占式调度。在协作式调度中,Goroutine需要主动让出CPU,例如通过调用系统调用、channel操作等。而抢占式调度允许在Goroutine执行时,由调度器在合适的时机(如函数调用、循环等边界)强制暂停Goroutine,将CPU资源分配给其他Goroutine,从而避免某个Goroutine长时间占用CPU。

不同场景下策略的协同工作及性能提升

  1. 高并发I/O场景
    • 假设一个Web服务器程序,有大量的HTTP请求处理Goroutine。当某个Goroutine进行I/O操作(如读取HTTP请求数据、写入响应数据等)时,会陷入阻塞。
    • 基于M:N调度模型,该Goroutine不会阻塞对应的M线程。M线程会解绑当前P,去寻找其他可运行的Goroutine(从P的本地队列或全局队列中获取),从而充分利用CPU资源。
    • 例如,在处理一个请求时,Goroutine需要从数据库读取数据,此时它会阻塞。调度器会将M线程重新分配给其他等待执行的Goroutine,如处理新的HTTP请求,提高了服务器的整体并发处理能力。
  2. CPU密集型场景
    • 对于一些计算密集型的任务,如科学计算、加密运算等。如果没有抢占式调度,一个CPU密集型的Goroutine可能会长时间占用CPU,导致其他Goroutine无法执行。
    • 有了抢占式调度后,调度器会在合适的时机(如函数调用边界)暂停当前CPU密集型的Goroutine,将CPU资源分配给其他Goroutine。例如,一个计算素数的Goroutine在执行一个复杂的循环计算时,调度器会在循环中的函数调用处暂停它,让其他Goroutine有机会运行,从而提高了程序整体的响应性和资源利用率。
  3. 任务均衡场景
    • 在P的本地队列中,Goroutine是按照FIFO(先进先出)的顺序执行的。当一个P的本地队列中的Goroutine执行完后,M线程会尝试从全局队列中获取Goroutine。
    • 同时,当一个P的本地队列中有太多Goroutine时,会进行“偷取”操作。例如,某个P上的任务特别繁重,其他空闲的P对应的M线程可以从这个繁忙的P的本地队列中“偷取”一半的Goroutine到自己的本地队列中执行,从而实现任务在各个P之间的均衡分配,提高整体性能。