I don't seem to totally understand maps in Go.
I have this code:
fetch := map[string]int{some data}
for condition {
fetchlocal := map[string]int{}
for key, value := range fetch {
if condition {
fetchlocal[key] = value
}
}
go threadfunc (fetchlocal)
}
Now wherever the threadfunc function uses the fetchlocal variable Go (go -race) says warning: data race. I also got a few panics already. But why? The fetchlocal variable isn't used by any other goroutine.
Can someone enlighten me, please?
The only way to avoid data races is to synchronize access to all mutable data that is shared between threads. There are several ways to achieve this. In Go, you would normally use a channel or a lock. (Lower-lever mechanisms are available in the sync and sync/atomic packages.)
We will use a Mutex to prevent the race condition. We know the data race occurs due to multiple goroutines accessing a shared variable. We can avoid this by locking access to our counter variable when one of the goroutines reads it, and then unlocking it when it is done writing the incremented value.
๐ Copy a map in Go Maps in Go are reference types, so to deep copy the contents of a map, you cannot assign one instance to another. You can do this by creating a new, empty map and then iterating over the old map in a for range loop to assign the appropriate key-value pairs to the new map.
Go has a built-in race detector that can be used to instrument the code at compile time and detect the races during their execution. Internally, the Go race detector uses the ThreadSanitizer runtime library which uses a combination of lock-set and happens-before based algorithms to report races.
I'm assuming your fetch := map[string]int{some data}
was actually supposed to be: fetch := map[string][]int{..some data..}
.
For this to be a race threadfunc
must be changing a value within fetchlocal
or something else must be changing the value within fetch
.
This is to say a slice is a actually:
type SliceHeader struct {
Data uintptr
Len int
Cap int
}
When you are copying the elements from one map to another you are not doing a deep copy of the slices (you are just creating a new struct with the same Data,Len,Cap), that is so say fetch["foo"].Data == fetchlocal["foo"].Data
.
Therefore you can say fetch[someExistingKey] = someNewValue
and this will not race with threadfunc
, but if you say fetch[someExistingKey][x] == foobar
or fetchlocal[someExistingKey][x] == foobar
the race will be on.
If fetchlocal
needs to mutated by threadfunc
you could change your inner loop to look like:
for key, value := range fetch {
if condition {
newVal := make([]int, len(value))
copy(newVal, val)
fetchlocal[key] = newVal
}
}
Or alternatively, do the copy inside threadfunc
as needed before mutating.
P.S. If you shared your actual threadfunc
or code that is modifying fetch
while these two loops are running, we will be able to help more.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With