go · level 3 · unit 6 · lesson 3
- meet it
- guess first
- your turn
- fix it
- nailed it
Tap a green word if you get lost.
meet it
A goroutine stuck forever is a leak. Give every one a way out.
A goroutine that can never finish is a . It waits forever on a send nobody will receive, holding on to its memory. In a server that runs for weeks, leaks pile up until the program falls over. Every goroutine you start needs a way to end.
The habit that prevents it: pass ctx context.Context as the first parameter of anything that starts long work, and put every send inside a select with case <-ctx.Done(): return. Then one cancel() frees every goroutine still waiting.
To spot leaks, compare runtime.NumGoroutine() before and after. In a test, a wg.Wait() that returns proves every goroutine finished, and one that never returns has found a leak.
package main
import (
"context"
"fmt"
)
func numbers(ctx context.Context) <-chan int {
out := make(chan int)
go func() {
for n := 1; ; n++ {
select {
case out <- n:
case <-ctx.Done():
return
}
}
}()
return out
}
func main() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
nums := numbers(ctx)
fmt.Println(<-nums, <-nums, <-nums)
} When main returns, the deferred cancel() lets the generator stop instead of waiting forever.
guess first
Three generators, one number each. How many leak?
Guess before you run it. Wrong guesses are how you learn, and nothing is marked.
package main
import (
"fmt"
"runtime"
)
func numbers() <-chan int {
out := make(chan int)
go func() {
for n := 1; ; n++ {
out <- n
}
}()
return out
}
func main() {
before := runtime.NumGoroutine()
for i := 0; i < 3; i++ {
<-numbers()
}
fmt.Println("leaked:", runtime.NumGoroutine()-before)
}
your turn
Your turn. Call off every download.
You changed your mind before any mirror answered. Fill in the method that gives each goroutine its way out, then the call that sets them free.
output
press run to see what happens
fix it
Two mirrors are stuck for good. Can you fix it?
output
press run to see what happens
wg.Wait()never returns. Only one answer is received, so what happens to the other two senders?- An unbuffered send waits for a receiver forever. Room in a buffer would let every send finish.
- Give the channel room for all three answers:
make(chan string, 3).
nailed it
every goroutine you start can finish. :D
- A goroutine waiting forever is a goroutine leak.
- Pass
ctxfirst and wrap every send in aselect. - A buffer big enough for every send is another way out.
new words in your collection
- goroutine leak
This lesson isn’t saved. A free account remembers every lesson you finish.