Lesson 11 · 11 of 20
Goroutines and WaitGroup · Go
Run work in parallel with goroutines in Go, wait for them to finish with sync.WaitGroup, and still get the same output every time.

A goroutine is a function running at the same time as the rest of the program. Just write `go` in front of the call: go func(i int) { ... }(i) starts one per document, and the loop carries on without waiting. They are very lightweight; a program can have thousands.
The catch is that main waits for nobody: when it returns, the program ends with the goroutines half done. `sync.WaitGroup` keeps count: wg.Add(1) before starting each one, defer wg.Done() inside it to report back whatever happens, and wg.Wait() blocks main until the count is back to zero.
Each goroutine writes only to its own slot, counts[i]. Since none of them touches another's slot, they share nothing and no lock is needed. If they all added to the same variable there would be a data race and the total could come out wrong; the mutex lesson deals with that.
i is passed as an argument to the anonymous function. Before Go 1.22 the loop variable was a single variable that kept changing, and a goroutine reading it directly might already see the value from another iteration. With the argument, each goroutine gets its own copy.
The goroutines finish in an order that changes from run to run, but the output does not: it is printed after wg.Wait(), walking counts in order. That is the rule for deterministic results: compute in parallel, then print at the end in a fixed order.
Code · main.go
package mainimport ( "fmt" "strings" "sync")func main() { names := []string{"intro.txt", "setup.txt", "faq.txt"} texts := []string{ "Go runs functions concurrently with goroutines", "install the toolchain, then build and run the program", "why share memory by communicating instead of communicating by sharing memory", } counts := make([]int, len(texts)) // one slot per goroutine: nothing is shared var wg sync.WaitGroup for i := range texts { wg.Add(1) // one more goroutine to wait for go func(i int) { // i is passed as an argument: each goroutine has its own copy defer wg.Done() counts[i] = len(strings.Fields(texts[i])) }(i) } wg.Wait() // blocks until every goroutine has called Done total := 0 for i, n := range counts { // printed in order, whatever order they finished in fmt.Printf("%-10s %2d words\n", names[i], n) total += n } fmt.Println("total:", total, "words in", len(texts), "documents")}Output · go run main.go
intro.txt 6 words setup.txt 9 words faq.txt 11 words total: 26 words in 3 documents
Key points
go f()runsfin a new goroutine.wg.Add(1)before,defer wg.Done()inside,wg.Wait()to wait.- If each goroutine writes to its own slot, nothing is shared.
- Pass the loop variable to the goroutine as an argument.
- Compute in parallel, print at the end, in order.
Exercise
Make each goroutine also find the longest word in its text, store it in a second slice, longest, and print it next to the word count.