Go Tutorial · Concurrency · Lesson 23 of 30
Go Goroutines and WaitGroup
Run functions concurrently with go, and wait for them with sync.WaitGroup.
Goroutines
Put go in front of a function call and it runs concurrently in a goroutine — a lightweight thread managed by the Go runtime. You can start thousands of them cheaply.
Waiting with WaitGroup
main doesn't wait for goroutines. Use a sync.WaitGroup to wait until they're all done:
package main
import (
"fmt"
"sync"
"time"
)
func main() {
urls := []string{"a.example", "b.example", "c.example"}
results := make([]string, len(urls))
var wg sync.WaitGroup
for i, u := range urls {
wg.Add(1)
go func() {
defer wg.Done()
time.Sleep(50 * time.Millisecond) // pretend to fetch
results[i] = u + ": 200 OK"
}()
}
wg.Wait()
for _, r := range results {
fmt.Println(r)
}
}
Output
a.example: 200 OK b.example: 200 OK c.example: 200 OK
Run this on your computer — see “Get Started” for setup.
Goroutines run in an unpredictable order, so this example writes each result into its own slot to keep the output stable.
Protecting shared data
When goroutines update shared data, protect it with a sync.Mutex (or use channels). The race detector — go run -race — finds unsafe access:
package main
import (
"fmt"
"sync"
)
func main() {
var (
mu sync.Mutex
total int
wg sync.WaitGroup
)
for range 100 {
wg.Add(1)
go func() {
defer wg.Done()
mu.Lock()
total++
mu.Unlock()
}()
}
wg.Wait()
fmt.Println("total:", total)
}
Output
total: 100
Run this on your computer — see “Get Started” for setup.
Test yourself
Which keyword starts a goroutine?
asyncgospawnthread
Show answer
Answer: go