Goroutines
What you'll learn:
- explain how concurrency differs from parallelism, and a goroutine from a thread;
- start goroutines and wait for them with
sync.WaitGroup; - pass values between goroutines over channels, close channels and wait on several channels with
select; - protect shared data from a data race with
sync.Mutex.

Picture a restaurant kitchen. One cook can make three dishes at once: put the soup on, chop the salad while it simmers, then go back to the soup. At any moment the cook does one thing, yet all three dishes move forward. Three cooks at three stoves work differently: each one is busy with a dish at the same instant.
Concurrency and parallelism
These two pictures are two different ideas.
Concurrency
Concurrency is when a program is made of several independent tasks that make progress interleaved. This is one cook with three dishes.
Concurrency is about how a program is structured: which tasks it consists of.
Parallelism
Parallelism is when several tasks literally run at the same time, on different processor cores. This is three cooks at three stoves.
A core is the part of the processor that runs one sequence of instructions at a time. Parallelism is about how a program runs.
How they differ
Interviewers like to ask this: "How does concurrency differ from parallelism?" The answer has three points:
- concurrency is about how a program is structured: which tasks it consists of;
- parallelism is about how it runs;
- a concurrent program may run in parallel on several cores, while on one core its tasks take turns.
Rob Pike, one of Go's authors, put it this way: concurrency is about dealing with lots of things at once, and parallelism is about doing lots of things at once.
Goroutines and threads
In Go, every cook in this kitchen is a goroutine: a function that runs at the same time as the rest of the program. The main function also runs in a goroutine, the main one.
A thread
The operating system runs programs in threads. A thread is expensive:
- about a megabyte of memory or more is usually reserved for it up front;
- the operating system itself creates and switches threads.
A goroutine
A goroutine starts with a few kilobytes and takes more memory only when it needs it. That is why a Go program can keep thousands or even hundreds of thousands of goroutines.
The Go runtime
The Go runtime, code that Go adds to every program, hands out the goroutines. It works like this:
- it spreads many goroutines over a few threads;
- by default, as many threads run Go code at once as there are cores available to the program;
- meanwhile the other goroutines wait their turn, the way cooks wait for a free stove.
This is where the kitchen picture stops working. A real head chef knows in advance who takes the stove after whom. The Go runtime promises nothing: which goroutine runs when may change from one run to the next.
The go statement
A Go program runs calls one at a time: the next line waits until the function returns. The go keyword in front of a call changes that rule. The call starts in a new goroutine, and main moves on without waiting for it:
package main
import "fmt"
func say(word string) {
fmt.Println(word)
}
func main() {
go say("hello")
fmt.Println("main")
}Let's see what happens here:
go say("hello")hires a new cook and returns at once.- The arguments are evaluated right away, on the line with
go, and the body ofsayruns in the new goroutine. - Any results the function returns are discarded.
fmt.Println("main")printsmain, andmainreturns.
The program prints the line main on every run, but hello only sometimes. The whole program ends together with main. If say has not reached fmt.Println by then, its output is lost: the kitchen closed with the cooks inside.
A goroutine from an anonymous function
The go statement works with any call:
- of a function;
- of a method;
- of an anonymous function.
A goroutine often runs an anonymous function. The parentheses after its body are the call:
for i := range 3 {
go func() {
fmt.Println(i)
}()
}Here is what happens:
- Each iteration starts its own goroutine, and each one sees its own value of
i. maindoes not wait for them here either.- The loop may print
0,1and2in any order, only some of the numbers, or nothing.
You cannot rely on the order in which goroutines run.
Goroutines in the browser
The course exercises run in the browser, where a program has one core. Concurrency works there, but parallelism does not: goroutines take turns. Their order is not fixed there either.
Common mistake
An anonymous function was passed to go but not called—the parentheses after the body are missing:
go func() {
fmt.Println("hello")
} // ✗expression in go must be function callThe program does not compile: the go statement needs a function call, not the function itself. The fix is to put () after the closing brace: }().
Key points
- Concurrency is how a program is split into tasks; parallelism is running them at the same moment on different cores.
- A goroutine is much cheaper than a thread: the Go runtime spreads many goroutines over a few threads.
go f()startsfin a new goroutine; whenmainreturns, the program ends without waiting for the others.- The order in which goroutines run is not fixed and may change from run to run.
Subscribe to take the course
The course is free. Sign in with Telegram and subscribe to the channel to open every lesson.