Go Goroutine and GMP

Goroutines are a crucial aspect of Go's support for concurrent programming. Conceptually similar to threads but much lighter, they are automatically scheduled and managed by Go's runtime system. Operating systems typically allocate 2MB of stack space per thread, whereas Goroutines initially require a significantly smaller stack (typically 2KB, with a maximum potential growth up to 1GB). Each Goroutine has its own stack space that can dynamically grow or shrink as needed, effectively reducing memory usage.

Main Goroutine and Sub-goroutines

In Go, when a program starts, a default goroutine (the main goroutine) is created for the main function. When the main goroutine (the main function) exits, the entire program exits, causing all other sub-goroutines to be forcibly terminated. In practical applications, to ensure that all goroutines (main and sub-goroutines) can complete their execution normally, mechanisms such as channels, select, sync.WaitGroup, etc., are used to synchronize and manage the lifecycle of goroutines.

func hello() {
    fmt.Println("This is hello goroutine!") //Non-execution Opportunities
}
func main() {
    go hello()
    fmt.Println("This is main goroutine!")
}
#This is main goroutine!

GMP

GMP is the core mechanism for implementing concurrent execution in Go, comprising G (Goroutine), M (Machine or kernel thread), and P (Processor). This model design enables Go to efficiently implement concurrent programming while maintaining lightweight thread management and resource utilization.

Goroutine(G)

  1. A Goroutine is a lightweight thread in Go, also known as a user-space thread or coroutine.
  2. Compared to traditional threads, the cost of creating and destroying Goroutines is very low, and the overhead of switching between them is minimal due to their scheduling by Go's runtime system in user space, rather than by the operating system kernel.
  3. Each Goroutine has its own stack, which can dynamically grow or shrink as needed.
  4. Thanks to their lightweight nature, Go can easily create thousands of Goroutines to concurrently execute tasks.

Machine(M)

  1. M represents a genuine operating system thread (kernel thread).
  2. Each M is scheduled by the operating system and has a fixed-size memory stack for executing C code.
  3. In Go's runtime system, M is responsible for executing the code of Goroutines. To reduce the overhead of thread creation and destruction, Go's runtime system tries to reuse M.
  4. After an M finishes executing a Goroutine, it fetches another Goroutine from the local run queue of other Ps to continue execution, thus achieving thread reuse.

Processor(P)

  1. P is a resource in Go's runtime and can be seen as the context environment required for executing Goroutines.
  2. The number of Ps determines the maximum number of Goroutines that can run simultaneously in the system. By default, the number of Ps is equal to the number of logical processors on the machine, to fully utilize the advantages of multicore processors, and can be configured via runtime.GOMAXPROCS().
  3. Each P has a local run queue for storing Goroutines waiting to be executed. When a new Goroutine is created, it is placed in the local run queue of a P to await execution.
  4. After an M finishes executing a Goroutine, it fetches a new Goroutine from the current P's local run queue to continue execution. If the local run queue of the current P is empty, M will attempt to fetch a Goroutine from the local run queue of other Ps or the global run queue to execute.

    Go Goroutine and GMP

The GMP model works by having P maintain the queues of Goroutines, M execute the code of Goroutines, and Go's runtime system managing the pairing relationship between M and P. When multiple Ps exist in the system, Go's runtime system dynamically adjusts the pairing relationship between M and P based on the current load to achieve efficient concurrent execution.

 

Take a break

👉👉👉 【BTTH Year EP104】Xiao Yan vs Feng Qing'er on the Four Pavilions Grand Meeting