Not understanding race problems

Race problems can be among the hardest and most insidious bugs to fix.

Multiple goroutines data races

Mistake

i := 0

go func() {
  i++
}()

go func() {
  i++
}()

Fix

Using atomic operations

var i int64

go func() {
  atomic.AddInt64(&i, 1)
}()

go func() {
  atomic.AddInt64(&i, 1)
}()

Using mutexes

i := 0
mutex := sync.Mutex{}

go func() {
  mutex.Lock()
  i++
  mutex.Unlock()
}()

go func() {
  mutex.Lock()
  i++
  mutex.Unlock()
}()

Using channels

i := 0
ch := make(chan int)

go func() {
  ch <- 1
}()

go func() {
  ch <- 1
}()

i += <-ch
i += <-ch

Channel data races

Mistake

Synchronizing

i := 0
go func() {
  i++ // Racer
}()

fmt.Println(i) // Racer

Buffered channels

i := 0
ch := make(chan struct{}, 1) // buffered

go func() {
  i = 1
  <-ch // A buffered receive happens before a send
}()

ch <- struct{}{}
fmt.Println(i)

Fix

Synchronizing

i := 0
ch := make(chan struct{})

go func() {
  <-ch // this receive blocks until channel send
  fmt.Println(i)
}()

i++
// alternatively we could close this channel for the same effect
ch <- struct{}{} // channel send

Buffered channels

i := 0
ch := make(chan struct{}) // unbuffered

go func() {
  i = 1
  <-ch
}()

ch <- struct{}{}
fmt.Println(i)

References