英文:
Data race in Go: Why does it happen below 10-11ms?
问题
这是我运行的代码:
package main
import (
"fmt"
"time"
)
const delay = 9 * time.Millisecond
func main() {
n := 0
go func() {
time.Sleep(delay)
n++
}()
fmt.Println(n)
}
这是我使用的命令:
go run -race data_race_demo.go
我注意到的行为是:
- 当
delay
设置为9ms或更低时,总是检测到数据竞争(程序抛出Found 1 data race(s)
) - 当
delay
设置为12ms或更高时,从不检测到数据竞争(程序只是打印0
) - 当
delay
设置为10到11ms时,数据竞争会间歇性地发生(即有时打印0
,有时抛出Found 1 data race(s)
)
为什么会在大约10-11ms时发生这种情况?
我在darwin/amd64
上使用的是Go 1.16.3,如果有关系的话。
英文:
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 throwsFound 1 data race(s)
) - With
delay
set to 12ms or higher, data race is never detected (program simply prints0
) - With
delay
set to 10 to 11ms, data race occurs intermittently (that is, sometimes prints0
and sometimes throwsFound 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.
答案1
得分: 7
你有两个goroutine:main
和你启动的另一个。它们在没有同步的情况下访问n
变量(其中一个是写入操作),这就是数据竞争。
是否检测到此竞争取决于是否发生了这种有竞争的访问。当main()
函数结束时,你的应用程序也会结束,它不会等待其他非main
的goroutine完成。
如果你增加睡眠延迟,main()
函数会在睡眠结束之前结束,并且不会等待n++
的竞争写入发生,因此不会检测到任何问题。如果睡眠时间很短,比fmt.Println()
的执行时间还要短,那么竞争写入就会发生并被检测到。
10毫秒没有什么特殊之处,那只是大致执行fmt.Println()
并在你的环境中终止应用程序所需的时间。如果在Println()
语句之前执行其他"耗时"的任务,例如:
for i := 0; i < 5_000_000_000; i++ {
}
fmt.Println(n)
即使睡眠时间为50毫秒,竞争仍然会被检测到(因为该循环需要一些时间来执行,允许在读取n
进行fmt.Println()
调用和应用程序终止之前发生竞争写入)。(简单的time.Sleep()
也可以实现,我只是不希望有人错误地得出它们以某种方式"相互作用"的错误结论。)
英文:
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.)
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论