Go: Goroutine, OS Thread and CPU Management

Vincent
A Journey With Go
Published in
5 min readNov 20, 2019

--

Illustration created for “A Journey With Go”, made from the original Go Gopher, created by Renee French.

ℹ️ This article is based on Go 1.13.

Creating an OS Thread or switching from one to another can be costly for your programs in terms of memory and performance. Go aims to get advantages as much as possible from the cores. It has been designed with concurrency in mind from the beginning.

M, P, G orchestration

To solve this problem, Go has its own scheduler to distribute goroutines over the threads. This scheduler defines three main concepts, as explained in the code itself:

The main concepts are:
G - goroutine.
M - worker thread, or machine.
P - processor, a resource that is required to execute Go code.
M must have an associated P to execute Go code[...].

Here is a diagram of this P, M, G model:

Each goroutine (G) runs on an OS thread (M) that is assigned to a logical CPU (P). Let’s take a simple example to see how Go manages them:

func main() {
var wg sync.WaitGroup
wg.Add(2)

go func() {
println(`hello`)
wg.Done()
}()

go func() {
println(`world`)
wg.Done()
}()

wg.Wait()
}

Go will first create the different P based on the number of logical CPUs of the machine and store them in a list of idle P:

--

--