GO Runtime

Goroutine定义

Goroutine 是 Go 语言中的一个核心概念,它是实现轻量级线程的一种方式,允许程序同时执行多个函数或方法,从而实现并发执行代码。Goroutine 的设计目的是为了简化并发编程,让开发者能够以较低的开销创建成千上万个并发的执行流,而不会像传统的操作系统线程那样占用大量资源。

Goroutine 的定义要点包括:

  1. 轻量级:相比操作系统线程,Goroutine 的创建和上下文切换开销极小,这使得在 Go 程序中可以轻松创建大量的 Goroutine。

  2. 用户态调度:Goroutines 由 Go 语言的运行时(runtime)管理,其调度完全在用户态完成,无需操作系统介入,提高了效率。

  3. 并发执行:Goroutines 允许在同一地址空间中并行执行多个函数或方法,共享内存空间的同时,通过通道(channel)等机制进行安全的通信。

  4. 启动方式:通过在任何函数调用前加上关键字 go 来启动一个新的 Goroutine,例如 go myFunction()。这样,该函数就会在一个新的 Goroutine 中异步执行,而不会阻塞调用它的 Goroutine。

  5. 栈管理:Goroutine 的栈大小是动态的,初始时很小,但在需要时可以自动增长,这进一步减少了内存使用并优化了性能。

  6. 安全性考虑:虽然 Goroutines 提供了高度的并发能力,但需要注意的是,如果在 Goroutines 之间共享可变数据时,必须采取适当的同步措施来防止数据竞争和不一致的问题,比如使用互斥锁(mutex)或通过通道进行通信。

简而言之,Goroutine 是 Go 语言提供的一种高效率的并发执行模型,它极大地简化了并发程序的设计和实现,是Go语言能够高效处理并发任务的关键特性之一。


go GMP指的是什么?

Go语言中的GMP是指其并发模型的核心组件,分别代表:

  • G (Goroutine): Goroutine 是 Go 语言的轻量级线程,是一种用户态的并发执行单元。每个 Goroutine 都有一个关联的栈空间、状态以及要执行的任务函数。Goroutines 的创建和上下文切换成本很低,使得在Go程序中可以同时运行成千上万的并发任务。

  • M (Machine): M 代表操作系统级别的线程或者说是执行的上下文。在Go中,M是实际执行计算的工作单元,它们由操作系统管理,并且每个M都有自己的内存栈来执行系统调用或运行非Go代码。Go运行时尽量复用M,以减少线程创建和销毁的开销。

  • P (Processor): P 代表逻辑处理器,也称为调度器。P负责将可运行的Goroutine分发到M上执行,并维护一个本地的Goroutine队列。P的数量通常是根据系统的CPU核心数来设定的,以充分利用多核处理器的优势。P还包含了调度所需的各种缓存和状态信息。

GMP模型的工作原理是这样的:Goroutines(G)被P(Processor)调度并在M(Machine)上执行。当一个G需要执行时,它会被分配到一个P,然后P再将其放到一个M上运行。如果所有的M都在使用中,新的G会被放入全局的运行队列等待,直到某个M可用。这种设计使得Go能够高效地管理并发任务,自动平衡负载,并在必要时跨线程迁移Goroutines,从而充分利用系统资源。


GMP 调度流程

Go语言中的GMP调度流程涉及三个核心实体:Goroutines(G)、操作系统线程(M)和逻辑处理器(P),它们协同工作以实现高效的并发执行。以下是GMP调度的基本步骤:

  1. 初始化:程序启动时,Go运行时会根据系统的CPU核心数创建一定数量的P(逻辑处理器),每个P都会绑定一个或多个M(操作系统线程)。此外,还会初始化一个全局的G队列,用于存放等待执行的Goroutine。

  2. Goroutine创建:当使用go关键字创建一个新的Goroutine时,Go运行时会为其分配一个G结构体,初始化相关信息,并将其放入到P的本地队列或全局队列中等待执行。

  3. 任务分配:每个M在执行时都会持有(绑定)一个P,M从P的本地G队列中选取Goroutine来执行。如果P的本地队列为空,M会尝试从其他P的本地队列偷取Goroutine,或者从全局队列中取出Goroutine。这一过程保证了Goroutines能在P之间均衡分配,充分利用CPU资源。

  4. 执行Goroutine:M选择一个Goroutine后,会将其状态设为“可执行”,然后在当前的M上开始执行。执行过程中,如果遇到阻塞操作(如I/O操作、通道读写等),Goroutine会进入阻塞状态,M会放弃当前的G并尝试从队列中选取另一个G继续执行,避免空闲。

  5. 上下文切换:在M需要切换执行的Goroutine时,会保存当前G的状态(如栈信息),恢复下一个要执行的G的状态,完成上下文切换。这个过程相对轻量,因为Goroutine的栈是可增长的,且大部分情况下不需要完整复制。

  6. 系统调用与线程管理:当Goroutine执行系统调用时,与其绑定的M可能会进入睡眠状态。此时,P会尝试将M与另一个就绪的Goroutine关联,或者如果没有就绪的G,则将M置为空闲。如果所有M都阻塞在系统调用上,Go运行时可能会创建新的M来继续执行Goroutines。

  7. 结束与回收:当Goroutine执行完毕,它会被标记为已完成,并从P的本地队列或全局队列中移除。M会检查是否有更多的G可以执行,如果没有,可能会尝试从其他地方偷取,或者自己进入休眠状态以节省资源。

这个流程是Go运行时自动管理的,为开发者提供了简洁的并发编程模型,隐藏了复杂的调度细节。


最后编辑: Simon  文档更新时间: 2024-05-16 21:31   作者:Simon