4

Here is the code I ran:

package main

import (
    "fmt"
    "time"
)

const delay = 9 * time.Millisecond

func main() {
    n := 0
    go func() {
        time.Sleep(delay)
        n++
    }()
    fmt.Println(n)
}

Here is the command I used:

go run -race data_race_demo.go

Here is the behavior I noticed:

  • With delay set to 9ms or lower, data race is always detected (program throws Found 1 data race(s))
  • With delay set to 12ms or higher, data race is never detected (program simply prints 0)
  • With delay set to 10 to 11ms, data race occurs intermittently (that is, sometimes prints 0 and sometimes throws Found 1 data race(s))

Why does this happen at around 10-11ms?

I'm using Go 1.16.3 on darwin/amd64, if that matters.

Manu Manjunath
  • 6,201
  • 3
  • 32
  • 31
  • 2
    "With delay set to 12ms or higher, data race is never detected" Which means nothing: The race is still present. – Volker May 21 '21 at 13:28

1 Answers1

7

You have 2 goroutines: the main and the one you launch. They access the n variable (and one is a write) without synchronization: that's a data race.

Whether this race is detected depends on whether this racy access occurs. When the main() function ends, your app ends as well, it does not wait for other non-main goroutines to finish.

If you increase the sleep delay, main() will end sooner than the end of sleep and will not wait for the n++ racy write to occur, so nothing is detected. If sleep is short, shorter than the fmt.Prinln() execution time, the racy write occurs and is detected.

There's nothing special about the 10ms. That's just the approximated time it takes to execute fmt.Println() and terminate your app in your environment. If you do other "lengthy" task before the Println() statement, such as this:

for i := 0; i < 5_000_000_000; i++ {
}
fmt.Println(n)

The race will be detected even with 50ms sleep too (because that loop will take some time to execute, allowing the racy write to occur before n is read for the fmt.Println() call and the app terminated). (A simple time.Sleep() would also do, I just didn't want anyone to draw the wrong conclusion that they "interact" with each other somehow.)

icza
  • 389,944
  • 63
  • 907
  • 827
  • Thanks. But, what's so special about 10ms? – Manu Manjunath May 21 '21 at 12:23
  • 2
    @ManuManjunath There's _nothing_ special about 10ms. That's just the time it takes to execute `fmt.Println()` and terminate your app in _your_ environment. – icza May 21 '21 at 12:25
  • You don't even have to do anything before the `fmt.Println`, just allowing the goroutine to run before exiting will show the race, even if you wait multiple seconds between the read and write. – JimB May 21 '21 at 13:17