详细叙述Golang中的Goroutine调度策略 ?

Go语言的并发模型基于goroutines和channels。goroutine是Go语言运行时环境中的轻量级线程,其主要特点是创建和销毁的代价非常小,可以方便地创建大量的goroutine来处理并发任务。然而,如何有效地调度这些goroutine,使它们能够在有限的硬件资源上运行,就需要依赖于Go的调度器。

Go的调度器采用了M:N调度模型,其中M代表的是用户级别的线程(也就是goroutine),而N代表的是内核级别的线程。Go调度器的主要任务就是在N个OS线程(也被称为M)上调度M个goroutine。这种模型允许在少量的OS线程上运行大量的goroutine。

Go的调度器使用了三种队列来管理goroutine:

  1. 全局队列(Global Queue):此队列中包含了所有刚创建的goroutine。

  2. 本地队列(Local Queue):每个P(Processor,处理器)都有一个本地队列,P会优先从本地队列中取出goroutine来执行。

  3. 网络轮询器(Netpoller):此队列中包含了所有在等待网络事件(如IO操作)的goroutine。当网络事件就绪时,对应的goroutine会被放入全局队列中,等待被P取出执行。

Go的调度器采用了工作窃取(Work Stealing)和手动抢占(Preemption)的策略:

  • 工作窃取:当一个P的本地队列中没有goroutine时,它会尝试从全局队列或其他P的本地队列中窃取goroutine来执行。

  • 手动抢占:为了防止一个goroutine长时间占用P而导致其他goroutine饿死,Go的调度器会定期的进行抢占操作。在Go 1.14之前,Go的调度器只在函数调用时才会进行抢占操作。从Go 1.14开始引入了异步抢占,即允许在任何安全点进行抢占。

这种调度模型和策略使Go语言可以有效的利用硬件资源,处理大量的并发任务,同时也为复杂的并发编程提供了简洁的语言级别的支持。

发表评论

后才能评论