I am not sure why mutex is not working as I expected. Any advice will help me.
Here is my code.
package main
import (
"fmt"
"sync"
"time"
)
type Container struct {
mu sync.Mutex
counters map[string]int
}
func (c *Container) inc(name string) {
c.mu.Lock()
defer c.mu.Unlock()
c.counters[name]++
// fmt.Println("in", name, c.counters)
// This print is doing tricks between goroutines
time.Sleep(time.Second)
}
func main() {
c := Container{
counters: map[string]int{"a": 0, "b": 0},
}
var wg sync.WaitGroup
doIncrement := func(name string, n int) {
for i := 0; i < n; i++ {
c.inc(name)
fmt.Println(name, c.counters)
}
wg.Done()
}
wg.Add(3)
go doIncrement("a", 2)
go doIncrement("b", 2)
go doIncrement("a", 2)
wg.Wait()
fmt.Println(c.counters)
}
When I ran this, I got strange outputs.
a map[a:2 b:0]
a map[a:2 b:0]
b map[a:2 b:1]
a map[a:4 b:1]
a map[a:4 b:1]
b map[a:4 b:2]
map[a:4 b:2]
I expected some logs where I can see a
increased to 1,2,3,4
When I removed comments in inc
function;
I could see the expected logs.
in a map[a:1 b:0]
a map[a:1 b:0]
in a map[a:2 b:0]
a map[a:2 b:0]
in b map[a:2 b:1]
b map[a:2 b:1]
in a map[a:3 b:1]
a map[a:3 b:1]
in a map[a:4 b:1]
a map[a:4 b:1]
in b map[a:4 b:2]
b map[a:4 b:2]
map[a:4 b:2]
In this loop:
for i := 0; i < n; i++ {
c.inc(name) ---> This runs with mutex locked
fmt.Println(name, c.counters) --> This runs with mutex unlocked
}
The Println runs outside the mutex lock. Two goroutines attempt to increment "a" at the same time, one increments and then waits. When that increment function returns, the second one goes in and increments, then Println from the first one runs, then Println from the second one prints the same thing.
So, mutex is working as expected, but you are printing outside the region protected by mutex.