Lightweight concurrent functions started with the go keyword.
The go Keyword
A goroutine is a function running concurrently with the rest of your program.
You start one by prefixing a function call with go.
go work()
That's it. The runtime takes care of scheduling thousands of goroutines onto a handful of OS threads. Goroutines are tiny — a few kilobytes each — so you can have millions at once.
A first goroutine
The two say calls run side by side. The output is interleaved.
The big "but" — main doesn't wait
If main returns, the whole program ends, even if other goroutines are still
running. You usually need a way to wait for the goroutines you started:
- a channel — preferred, you'll see it in the next chapter,
- a
sync.WaitGroup, - or just
time.Sleepfor demos.
Run that — most likely nothing prints. Now run this:
WaitGroup is a small counter — Add(n) increases it, Done() decreases it,
Wait() blocks until it reaches zero.
Concurrency vs parallelism
Goroutines are concurrent — they're logically independent. Whether they run
in parallel depends on how many CPU cores Go uses (which you can read and
set via runtime.NumCPU() and GOMAXPROCS, but the default is "all cores",
which is what you want).
Don't communicate by sharing memory; share memory by communicating.
— A Go proverb. The "communicating" bit is channels, the next chapter.
Goroutine leaks
A goroutine that blocks forever (waiting on a channel that nobody sends to, a mutex never released, a network read that never returns) stays alive until the program exits. The garbage collector cannot free it because it's still "running". This is called a leak and it's the most common concurrency bug in Go.
Always make sure every goroutine you start has a path to finish. The two common tools are:
context.Context— pass a cancellation signal in, return when it fires.- Closing channels —
rangeover a channel exits when the channel is closed.
We cover both later in this track.