Go Race Conditions & Mutexes
Links: 103 Golang Index
Data Race¶
- When 2 go routines are executing concurrently (at the same time) if there is no special handling then it can lead to race conditions.
- These are very difficult to debug.
- Race conditions occur because of unsynchronised access to memory.
-
Example of a race condition
import ( "fmt" "sync" ) func main() { var wg sync.WaitGroup gr := 100 wg.Add(gr * 2) value := 0 // we are not using wg pointers here since there is nothing to pass // we use pointers in functions because in go functions are call by value for i := 0; i < gr; i++ { go func() { defer wg.Done() value++ }() go func() { defer wg.Done() value-- }() } wg.Wait() fmt.Println(value) } // value can be anything // Value of n depends on which go routine finishes first
-
We can use built in race detector to find race conditions in go.
- To use it we pass it as a flag when running main.go :
go run -race main.go
- To use it we pass it as a flag when running main.go :
Mutexes¶
- Mutex comes from mutual exclusion
- This is called explicit synchronisation (from experience we realise this variable can be subjected to race condition) where variable access is protected through synchronisation primitives such as a mutex.
- Solving the above race condition using a mutex. This problem could also have been solved by a channel
import ( "fmt" "sync" ) func main() { var m sync.Mutex var wg sync.WaitGroup gr := 100 wg.Add(gr * 2) value := 0 // if we were using named functions then we would have to pass pointers to the mutex for i := 0; i < gr; i++ { go func() { defer wg.Done() m.Lock() value++ m.Unlock() }() go func() { defer wg.Done() m.Lock() value-- m.Unlock() }() } wg.Wait() fmt.Println(value) // even though we have used mutexes if fmt.Println() was above wg.Wait() then // it would have been a race condition }
Last updated: 2022-06-18