Knucklehead
Knucklehead

Reputation: 97

sync.WaitGroup not behaving as I expect, what am I missing here?

Given the following:

package main

import (
    "fmt"
    "sync"
)

func main() {
    n := 100

    var wg sync.WaitGroup
    wg.Add(n)

    x := 0
    for i := 0; i < n; i++ {
        go func() {
            defer wg.Done()
            x++
        }()
    }

    wg.Wait()
    fmt.Println(n, x)
}

I would expect x to always reach 100 by the time it prints at the end, but it sometimes prints as low as 95. What am I missing here?

Upvotes: 1

Views: 147

Answers (2)

Tony Bai
Tony Bai

Reputation: 151

use sync.atomic method to access x atomically.

package main

import (
    "fmt"
    "sync"
    "sync/atomic"
)

func main() {
    n := 100

    var wg sync.WaitGroup
    wg.Add(n)

    var x int32
    for i := 0; i < n; i++ {
        go func() {
            defer wg.Done()
            atomic.AddInt32(&x, 1)
        }()
    }

    wg.Wait()
    fmt.Println(n, x)
}

Upvotes: 1

Thundercat
Thundercat

Reputation: 120941

There's a race on x. One fix is to protect x with a mutex:

var mu sync.Mutex
var wg sync.WaitGroup
wg.Add(n)

x := 0
for i := 0; i < n; i++ {
    go func() {
        defer wg.Done()
        mu.Lock()
        x++
        mu.Unlock()
    }()
}

wg.Wait()
fmt.Println(n, x)

playground example

I suggest running the race detector whenever one finds something puzzling in a Go program with more than one goroutine.

Upvotes: 5

Related Questions