英文:
Understanding race condition in Go
问题
我有以下非常简单的代码:
for i := 0; i < 100; i++ {
tt := time.AfterFunc(10*time.Microsecond, func() { log.Fatal("bleh!") })
time.Sleep(2 * time.Microsecond)
tt.Stop()
}
现在,看起来它似乎什么都不会发生,当我在 Go Playground 上运行它时,它会按预期地静默退出,但是当我在我的 Macbook 上运行它时,我总是会遇到错误,例如:
go run example.go
2017/03/02 22:21:50 bleh!
2017/03/02 22:21:50 bleh!
exit status 1
我可以将 AfterFunc
中的时间设置得非常高,甚至高达 50 或 100 微秒,我仍然偶尔会得到 "bleh!" 和程序退出。Go 中的 time.Sleep
有什么保证?有没有更好的方法来测试某个操作是否在特定时间内运行?
英文:
I have the following very simple code:
for i := 0; i < 100; i++ {
tt := time.AfterFunc(10*time.Microsecond, func() { log.Fatal("bleh!") })
time.Sleep(2 * time.Microsecond)
tt.Stop()
}
Now, it should seem that it would do nothing, and when I run it on the Go playground, it just quietly exits as expected, but when I run this on my Macbook, I invariably get errors, like:
go run example.go
2017/03/02 22:21:50 bleh!
2017/03/02 22:21:50 bleh!
exit status 1
And I can put that time on the AfterFunc
quite high indeed, even as high as 50 or 100 μs and I'll still get the occasional "bleh!" and death. What are the guarantees around time.Sleep
in Go? And what is a better way of testing that something ran within a specific amount of time?
答案1
得分: 4
保证time.Sleep(2 * time.Microsecond)
至少会休眠2微秒,并且time.AfterFunc(10 * time.Microsecond, ...)
会在至少10微秒后调用其回调函数。没有保证上限(底层操作系统可以任意处理),也没有保证相对顺序——如果休眠时间超过10微秒,那么AfterFunc
可能会在其之前或之后执行。
如果使用相对较长的时间段,那么很有可能会观察到按正确顺序发生的情况,因为Sleep
的goroutine会在计时器到期之前被调度。但是,微秒级别的截止时间非常短,因此,不科学地说,在微秒级别彼此相近的截止时间很可能以任意顺序发生。
Playground使用了一个修改过的运行时来模拟时间,这也是测试的最佳方法。对于大多数人来说,修改运行时实际上并不实用,但你可以尝试使用clockwork包,该包提供了对真实和虚拟时钟对象的接口。否则,你可以自己采用相同的方法——如果你想要进行测试,就不能依赖于time
,因为time
是全局且不可重现的。相反,它必须从某个在生产中使用time
的组件中获取通知,但在测试时可以替换掉这个组件。
英文:
The guarantee is that time.Sleep(2 * time.Microsecond)
will sleep for at least 2 microseconds, and time.AfterFunc(10 * time.Microsecond, ...)
will call its callback after at least 10 microseconds. No upper limit is guaranteed (the underlying operating system could do anything it wants), and no relative ordering is guaranteed — if the sleep happens to take more than 10 microseconds, then the AfterFunc
could end up before or after it.
If you used reasonably long periods of time then it's very likely that you would observe things happening in the right order, because the Sleep
ing goroutine would get scheduled before the timer expired. But a microsecond is a very short period of time, and so, unscientifically speaking, deadlines that happen within microseconds of each other are very likely to happen in whatever order they feel like.
The playground uses a modified runtime that fakes time, which incidentally is the best approach for testing as well. Modifying the runtime isn't really practical for most, but you might try the clockwork package which provides an interface to both real and fake clock objects. Otherwise, follow the same approach yourself — if you want something to be testable, it can't depend on time
, because time
is global and irreproducible. Instead, it has to get its notifications from some component that uses time
in production, but is replaceable for testing.
答案2
得分: 0
func Sleep
> Sleep函数会暂停当前的goroutine,至少暂停时间为d。如果传入的时间为负数或零,Sleep函数会立即返回。
这里最重要的是"至少"这个词。它意味着Sleep
函数可能会花费比传入的时间更长的时间。这种行为取决于操作系统的当前状态。
for i := 0; i < 100; i++ {
tt := time.AfterFunc(
10*time.Microsecond,
func() {log.Fatal("bleh!") }) // 有足够的时间被执行
time.Sleep(2 * time.Microsecond) // 睡眠时间超过了10微秒
tt.Stop()
}
英文:
func Sleep
> Sleep pauses the current goroutine for at least the duration d. A negative or zero duration causes Sleep to return immediately.
The most significant here is that "at least". It means Sleep
could take more time than a passed value. This behavior is dependent from a current state of os.
for i := 0; i < 100; i++ {
tt := time.AfterFunc(
10*time.Microsecond,
func() {log.Fatal("bleh!") }) // has time to be executed
time.Sleep(2 * time.Microsecond) // Slept more than 10 microsecond
tt.Stop()
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论