Essential Go Concurrency  Suggest an edit

Wait for goroutines to finish

Go programs end when the main function ends, therefore it is common practice to wait for all goroutines to finish.

A common solution for this is to use a sync.WaitGroup object.

var wg sync.WaitGroup // 1

func routine(i int) {
	defer wg.Done() // 3
	fmt.Printf("routine %v finished\n", i)
}

func main() {
	wg.Add(10) // 2
	for i := 0; i < 10; i++ {
		go routine(i) // *
	}
	wg.Wait() // 4
	fmt.Println("main finished")
}
routine 5 finished
routine 1 finished
routine 0 finished
routine 7 finished
routine 6 finished
routine 9 finished
routine 3 finished
routine 8 finished
routine 4 finished
routine 2 finished
main finished

WaitGroup usage in order of execution:

  1. Declaration of global variable. Making it global is the easiest way to make it visible to all functions and methods.
  2. Increasing the counter. This must be done in the main goroutine because there is no guarantee that a newly started goroutine will execute before 4 due to memory model guarantees.
  3. Decreasing the counter. This must be done at the exit of a goroutine. By using a deferred call, we make sure that it will be called whenever function ends, no matter how it ends.
  4. Waiting for the counter to reach 0. This must be done in the main goroutine to prevent the program from exiting before all goroutines have finished.

Parameters are evaluated before starting a new goroutine.

Thus it is necessary to define their values explicitly before wg.Add(10) so that possibly-panicking code will not increase the counter. Adding 10 items to the WaitGroup, so it will wait for 10 items before wg.Wait returns the control back to main() goroutine. Here, the value of i is defined in the for loop.

  ↑ ↓ to navigate     ↵ to select     Esc to close