Check data race


“Data race” is a common but notorious issue in concurrency programs. sometimes it is difficult to debug and reproduce, especially in some big system, so this will make people very frustrated. Thankfully, the Go toolchain provides a race detector (now only works on amd64 platform.) which can help us quickly spot and fix this kind of issue, and this can save our time even lives!

Take the following classic “data race” program as an example:

  1. package main
  2. import (
  3. "fmt"
  4. "sync"
  5. )
  6. var global int
  7. var wg sync.WaitGroup
  8. func count() {
  9. defer wg.Done()
  10. for i := 0; i < 10000; i++{
  11. global++
  12. }
  13. }
  14. func main() {
  15. wg.Add(2)
  16. go count()
  17. go count()
  18. wg.Wait()
  19. fmt.Println(global)
  20. }

Two tasks increase global variable simultaneously, so the final value of global is non-deterministic. Using race detector to check it:

  1. # go run -race race.go
  2. ==================
  3. WARNING: DATA RACE
  4. Read by goroutine 7:
  5. main.count()
  6. /root/gocode/src/race.go:14 +0x6d
  7. Previous write by goroutine 6:
  8. main.count()
  9. /root/gocode/src/race.go:14 +0x89
  10. Goroutine 7 (running) created at:
  11. main.main()
  12. /root/gocode/src/race.go:21 +0x6d
  13. Goroutine 6 (running) created at:
  14. main.main()
  15. /root/gocode/src/race.go:20 +0x55
  16. ==================
  17. 19444
  18. Found 1 data race(s)
  19. exit status 66

Cool! the race detector finds the issue precisely, and it also provides the detailed tips of how to modifying it. Adding the lock of writing the global variable:

  1. package main
  2. import (
  3. "fmt"
  4. "sync"
  5. )
  6. var global int
  7. var wg sync.WaitGroup
  8. var w sync.Mutex
  9. func count() {
  10. defer wg.Done()
  11. for i := 0; i < 10000; i++{
  12. w.Lock()
  13. global++
  14. w.Unlock()
  15. }
  16. }
  17. func main() {
  18. wg.Add(2)
  19. go count()
  20. go count()
  21. wg.Wait()
  22. fmt.Println(global)
  23. }

This time, race detector is calm:

  1. # go run -race non_race.go
  2. 20000

Please be accustomed to use this powerful tool frequently, you will appreciate it, I promise!

Reference:
Introducing the Go Race Detector.