Race condition even when using sync.Mutex in golang

The full code is here: https://play.golang.org/p/ggUoxtcv5m go run -race main.go says that there is a race condition, which I can not explain. However, the program displays the correct end result.

Essence:

type SafeCounter struct {
    c int
    sync.Mutex
}

func (c *SafeCounter) Add() {
    c.Lock()
    c.c++
    c.Unlock()
}

var counter *SafeCounter = &SafeCounter{} // global

use *SafeCounterin increment:

func incrementor(s string) {
    for i := 0; i < 20; i++ {
        x := counter
        x.Add()
        counter = x
    }
}

The method is incrementorgenerated twice in main:

func main() {
    go incrementor()
    go incrementor()
    // some other non-really-related stuff like
    // using waitGroup is ommited here for problem showcase
}

So, as I said, go run -race main.goit will always say that a race has been discovered.

In addition, the end result is always correct (at least I ran this program several times, and it always says that the last counter is 40, which is correct). BUT, the program prints the wrong values ​​at the beginning so you can get something like:

Incrementor1: 0 Counter: 2
Incrementor2: 0 Counter: 3
Incrementor2: 1 Counter: 4
// ang the rest is ok

1 .

- , , ?

+4
2

, :

    x := counter      // this reads the counter value without a lock
    fmt.Println(&x.c)
    x.Add()
    counter = x       // this writes the counter value without a lock
    time.Sleep(time.Duration(rand.Intn(3)) * time.Millisecond)
    fmt.Println(s, i, "Counter:", x.c) // this reads the c field without a lock
  • № 1 counter incrementor

  • № 2 counter incrementor

  • № 3 x.c fmt.Println x.c Add.

+7

, , goroutines.

func incrementor(s string) {
    for i := 0; i < 20; i++ {
        x := counter  // <-- this pointer read
        x.Add()
        counter = x   // <-- races with this pointer write
    }
}
+1

Source: https://habr.com/ru/post/1676321/


All Articles