I don’t think it thread-safe because map object is larger than a machine word, and Golang has no guarantee it’s thead-safe. But when I run the demo code with go run -race main.go
, it never reports errors. It might be the reason that ThreadSanitizer depends on runtime checking and assigning operation is hard to meet the thread unsafe condition.
Here is the sample code:
package main
import (
"fmt"
)
var m = make(map[int]bool)
func Read() {
for {
for k := range m {
fmt.Println(k)
}
}
}
func Replace() {
for {
newM := make(map[int]bool, 10)
for i := 0; i < 10; i++ {
newM[i] = false
}
m = newM
}
}
func main() {
c := make(chan struct{})
go Read()
go Replace()
<-c
}
So how could I modify the code to trigger the concurrent error? Or maybe I am wrong and the code is thread-safe?
>Solution :
There are several things to note here:
for k := range m {
Range expression is evaluated once at the beginning of the for loop. So this operation will read m
once, and the loop itself will call fmt.Println
, which consumes most of the execution time in that goroutine. If you want to catch the race, remove that.
Second, you don’t really need to initialize the second map.
And when you do these things and run the race detector, it may catch the data race. In my case, it did.
The race detector complains about a race when it detects one. Because of that, if it reports a race, then there is a race. If it doesn’t report one, that doesn’t mean there isn’t a race.
In my platform, a map variable itself is actually the same size as a machine word: it is simply a pointer to the map structure. So, the write to a map variable is actually atomic, that is, you would not see a partially-assigned map in this platform. However, that doesn’t prevent the race, because there are no guarantees on when the other goroutine will see that memory write.
In short, there is a race. It is not because of the sie of the map. To fix, use a mutex.