Waitgroups: Coordinating Concurrent Goroutines

GoGoBeginner
Practice Now

This tutorial is from open-source community. Access the source code

Introduction

The waitgroups lab is designed to help you understand how to use a wait group to wait for multiple goroutines to finish.


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL go(("`Go`")) -.-> go/ConcurrencyGroup(["`Concurrency`"]) go/ConcurrencyGroup -.-> go/waitgroups("`Waitgroups`") subgraph Lab Skills go/waitgroups -.-> lab-15529{{"`Waitgroups: Coordinating Concurrent Goroutines`"}} end

waitgroups

The problem to be solved in this lab is to launch several goroutines and increment the WaitGroup counter for each. Then, we need to wait for all the goroutines launched to finish.

  • Basic knowledge of Golang.
  • Understanding of concurrency in Golang.
  • Familiarity with the sync package.
$ go run waitgroups.go
Worker 5 starting
Worker 3 starting
Worker 4 starting
Worker 1 starting
Worker 2 starting
Worker 4 done
Worker 1 done
Worker 2 done
Worker 5 done
Worker 3 done

## The order of workers starting up and finishing
## is likely to be different for each invocation.

There is the full code below:

// To wait for multiple goroutines to finish, we can
// use a *wait group*.

package main

import (
	"fmt"
	"sync"
	"time"
)

// This is the function we'll run in every goroutine.
func worker(id int) {
	fmt.Printf("Worker %d starting\n", id)

	// Sleep to simulate an expensive task.
	time.Sleep(time.Second)
	fmt.Printf("Worker %d done\n", id)
}

func main() {

	// This WaitGroup is used to wait for all the
	// goroutines launched here to finish. Note: if a WaitGroup is
	// explicitly passed into functions, it should be done *by pointer*.
	var wg sync.WaitGroup

	// Launch several goroutines and increment the WaitGroup
	// counter for each.
	for i := 1; i <= 5; i++ {
		wg.Add(1)
		// Avoid re-use of the same `i` value in each goroutine closure.
		// See [the FAQ](https://golang.org/doc/faq#closures_and_goroutines)
		// for more details.
		i := i

		// Wrap the worker call in a closure that makes sure to tell
		// the WaitGroup that this worker is done. This way the worker
		// itself does not have to be aware of the concurrency primitives
		// involved in its execution.
		go func() {
			defer wg.Done()
			worker(i)
		}()
	}

	// Block until the WaitGroup counter goes back to 0;
	// all the workers notified they're done.
	wg.Wait()

	// Note that this approach has no straightforward way
	// to propagate errors from workers. For more
	// advanced use cases, consider using the
	// [errgroup package](https://pkg.go.dev/golang.org/x/sync/errgroup).
}

Summary

In this lab, we learned how to use a wait group to wait for multiple goroutines to finish. We also learned how to launch several goroutines and increment the WaitGroup counter for each. Finally, we saw how to wait for all the goroutines launched to finish.

Other Go Tutorials you may like