Race condition

  • A race condition occurs when two or more threads can access shared data and they try to change it at the same time.
  • Problems often occur when one thread does a “check-then-act” (e.g. “check” if the value is X, then “act” to do something that depends on the value being X) and another thread does something to the value in between the “check” and the “act”.
  • In order to prevent race conditions from occurring, you would typically put a lock around the shared data to ensure only one thread can access the data at a time.

-race

Enable data race detection.
Supported only on linux/amd64, freebsd/amd64, darwin/amd64, windows/amd64,
linux/ppc64le and linux/arm64 (only for 48-bit VMA).

See more in go cmd documentation

Test the effect

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// test.go

package main

import (
"fmt"
"sync"
)

var counter int

func main() {
wg := sync.WaitGroup{}
wg.Add(2)
go addToCounterTenTimes(1, &wg)
go addToCounterTenTimes(2, &wg)
wg.Wait()
}

func addToCounterTenTimes(add int, wg *sync.WaitGroup) {
for i := 0; i < 10; i++ {
counter += add
fmt.Println("counter:", counter)
}
wg.Done()
}
1
go run -race test.go

Output:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
counter: 1
counter: 2
counter: 3
counter: 4
counter: 5
counter: 6
counter: 7
counter: 8
counter: 9
counter: 10
==================
WARNING: DATA RACE
Read at 0x00000122fcb0 by goroutine 8:
main.addToCounterTenTimes()
/Users/anran/Desktop/AnranBlog/source/_posts/test.go:20 +0x4a

Previous write at 0x00000122fcb0 by goroutine 7:
main.addToCounterTenTimes()
/Users/anran/Desktop/AnranBlog/source/_posts/test.go:20 +0x66

Goroutine 8 (running) created at:
main.main()
/Users/anran/Desktop/AnranBlog/source/_posts/test.go:14 +0xda

Goroutine 7 (finished) created at:
main.main()
/Users/anran/Desktop/AnranBlog/source/_posts/test.go:13 +0xaf
==================
counter: 12
counter: 14
counter: 16
counter: 18
counter: 20
counter: 22
counter: 24
counter: 26
counter: 28
counter: 30
Found 1 data race(s)
exit status 66

Note that I had to put -race after go run to make it work. I developed a habbit of putting all flags at the end working with Docker, though this would not work - this will make -race an argument past to test.go instead of go run.