×

go · level 3 · unit 6 · lesson 3

  1. meet it
  2. guess first
  3. your turn
  4. fix it
  5. 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.