2

I'm running a function in a goroutine each time a for-loop iterates, and I'm using sync.WaitGroup to make sure the goroutines all finish. However, I'm getting weird behavior testing the concurrency with counters. In the example below, I attempt to keep track of the thread count using 4 different techniques (w, x, y, z), and get 4 different results. The only result I understand is x, since it is incremented in the for-loop itself. What am I missing here?

package main

import "fmt"
import "sync"

var w = 0

func main() {
  x := 0
  y := 0
  z := 0
  var wg sync.WaitGroup
  for i := 0; i < 10000; i++ {
    wg.Add(1)
    x++
    go func() {
      z++
      test(&y)
      wg.Done()
    }()
  }
  wg.Wait()
  fmt.Println(w, x, y, z) // 8947 10000 8831 8816
}

func test(y *int) {
  w++
  *y++
}
1
  • @CeriseLimón do I need to use a mutex to increment? what is the most efficient way to achieve this? Commented Dec 30, 2017 at 19:45

2 Answers 2

9

The sync.Waitgroup is working as expected. w, y and z will not reach 10000 because multiple goroutines are incrementing them concurrently, and Go's increment is not concurrent-safe: it is implemented as a normal fetch-increment-reassign operation.

You have two options.

option 1: mutex

type incrementer struct {
    sync.Mutex
    i int
}

func (i *incrementer) Add(n int) {
    i.Lock()
    defer i.Unlock()
    i.i += n
}

and use this type for w, y and z.

Full example: https://play.golang.org/p/6wWUK2xnOCW

option 2: sync.atomic

var w int32 = 0

go func(){
    // in the loop
    atomic.AddInt32(&w, 1)

}()

Full example: https://play.golang.org/p/oUCGgKYC1-Y

Sign up to request clarification or add additional context in comments.

Comments

0

The problem to your scenario is that go routines are not thread safe. In other words the values read y, z, w can be totally incorrect.

As another comment mentioned you should use mutex.

package main

import "fmt"
import "sync"

var w = 0

func main() {
  var m sync.Mutex

  x := 0
  y := 0
  z := 0
  var wg sync.WaitGroup
  for i := 0; i < 10000; i++ {
    wg.Add(1)
    x++
    go func() {
      m.Lock()
      defer m.Unlock()
      z++
      test(&y)
      wg.Done()
    }()
  }
  wg.Wait()
  fmt.Println(w, x, y, z) // 10000 10000 10000 10000
}

func test(y *int) {
  w++
  *y++
}

You can test it here: https://play.golang.com/

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.