Skip to content
Go Basics: Wait for Concurrent Work Before Reading Results

Go Basics: Wait for Concurrent Work Before Reading Results

October 31, 2026

Concurrent workers finish in an unpredictable order. Store each result in its assigned slot and wait before reading the slice, keeping the report deterministic without serializing the calculations.

Prepare a scratch directory

Tested on Linux amd64 with Go 1.26.10. Install Go and make go available on PATH. You also need Bash, a text editor, and the standard mktemp, rm, and rmdir utilities. The example uses only the standard library and needs no module or external dependency.

Keep the same terminal open. Stop if setup fails, before saving the source:

start_dir="$PWD"
work_dir="$(mktemp -d)" || exit 1
cd -- "$work_dir" || exit 1

Run the example

Save this as waitgroup-results-demo.go:

package main

import (
	"fmt"
	"sync"
)

func main() {
	inputs := []int{3, 1, 4}
	results := make([]int, len(inputs))
	var workers sync.WaitGroup
	for i, n := range inputs {
		workers.Add(1)
		go func(index, value int) {
			defer workers.Done()
			results[index] = value * value
		}(i, n)
	}
	workers.Wait()
	for i, result := range results {
		fmt.Printf("%d squared = %d\n", inputs[i], result)
	}
}

Run it:

go run waitgroup-results-demo.go

Observed output:

3 squared = 9
1 squared = 1
4 squared = 16

Read the result

Each worker owns one slice element and does not modify the slice header. Add happens before launching the goroutine, so Wait cannot miss a worker that has not started. The main goroutine reads the elements only after all Done calls have completed.

Apply it carefully

WaitGroup tracks completion, not error propagation, cancellation, or a concurrency limit. Do not append to the shared slice or let multiple workers write the same element without synchronization. For a large input, use a bounded worker pool instead of creating one goroutine per item.

The official Go reference documents the behavior used here.

Clean up

Remove the saved source, return to the starting directory, and remove the empty practice directory. go run does not leave an executable here; Go manages its build cache separately.

rm -- waitgroup-results-demo.go
cd -- "$start_dir" || exit 1
rmdir -- "$work_dir"
unset start_dir work_dir

If the directory is not empty, inspect its remaining contents before removing anything else.

Acknowledgements

This post was created with assistance from AI.

Last updated on