10 Aug 2015, 00:00

WaitGroup gotcha


Quick, what is wrong with this Go code?

    package main

import (

func main() {
    var wg sync.WaitGroup

    for i := 0; i < 100; i++ {
        go doWork(i, wg)

    log.Println("Starting to wait")

func doWork(i int, wg sync.WaitGroup) {
    defer wg.Done()
    sleepTime := time.Duration(rand.Int31n(10000000))
    log.Printf("%d slept for %d", i, sleepTime)

Answer - the WaitGroup is passed as a value. Internally WaitGroups are structs with a contained integer that gets atomically incremented and decremented. So by passing a value, the internal count gets copied and the copy gets decremented when Done() is called. So this is a deadlock. Fixed code here, simply pass it as a pointer. Helpfully Go will warn you about this:

$ go vet wg-gotcha.go

wg-gotcha.go:23: doWork passes Lock by value: sync.WaitGroup contains sync.Mutex

exit status 1

So, moral of the story: Don’t pass WaitGroups around by value, and always run go vet!