Go 调度器深度拆解:goroutine 到底对 CPU 做了什么

💡 原文中文,约7900字,阅读约需19分钟。
📝

内容提要

Go语言的调度器通过用户态实现M:N调度,支持百万个轻量级goroutine并发运行。其GMP模型中,G代表goroutine,M为操作系统线程,P是逻辑CPU资源。Go调度器采用FIFO队列和工作窃取策略,减少内核调度开销,提高创建和切换速度。与Linux CFS调度器相比,Go设计追求高吞吐量,适合处理大量并发任务。

🎯

关键要点

  • Go语言的调度器通过用户态实现M:N调度,支持百万个轻量级goroutine并发运行。

  • GMP模型中,G代表goroutine,M为操作系统线程,P是逻辑CPU资源。

  • Go调度器采用FIFO队列和工作窃取策略,减少内核调度开销,提高创建和切换速度。

  • Go的调度器在用户态实现,避免了内核调度的重量级操作,显著提高了性能。

  • Go的goroutine创建成本约为0.3微秒,内存占用约为20MB,而Linux CFS调度器则需要更高的开销。

  • Go的调度器设计追求高吞吐量,适合处理大量并发任务,而Linux CFS则追求公平性。

  • Go的调度器在处理I/O时采用非阻塞模式,避免了goroutine因阻塞而导致的资源浪费。

  • Go的抢占机制在1.14版本后引入了基于信号的异步抢占,提升了调度的灵活性。

  • Go调度器的设计哲学是简单规则、极低开销和runtime自动决策,适合大多数应用场景。

延伸问答

Go调度器的M:N调度模型是什么?

Go调度器通过用户态实现M:N调度,将多个goroutine映射到少量操作系统线程上,避免内核调度的开销。

Go的goroutine与Linux线程相比有什么优势?

Go的goroutine创建成本约为0.3微秒,内存占用约为20MB,而Linux线程创建成本较高,内存占用大约为8MB。

Go调度器如何处理I/O操作?

Go的调度器在处理I/O时采用非阻塞模式,避免goroutine因阻塞而导致的资源浪费,使用netpoller进行高效调度。

Go调度器的抢占机制是如何工作的?

Go 1.14引入了基于信号的异步抢占机制,定期检查goroutine运行时间,若超过10ms则发送信号进行抢占。

Go调度器与Linux CFS调度器的主要区别是什么?

Go调度器追求高吞吐量,采用FIFO队列和工作窃取策略,而Linux CFS调度器追求公平性,使用vruntime红黑树。

Go调度器的设计哲学是什么?

Go调度器的设计哲学是简单规则、极低开销和runtime自动决策,适合大多数应用场景。

➡️

继续阅读