让我们从GoTour中获取此示例,因为它说明了仅在存在事件时处理SDL事件的问题。
package main
import (
"fmt"
"time"
)
func main() {
tick := time.Tick(1e8)
boom := time.After(5e8)
for {
select {
case <-tick:
fmt.Println("tick.")
case <-boom:
fmt.Println("BOOM!")
return
default:
fmt.Println(" .")
time.Sleep(5e7)
}
}
}
这很有效。但是如果我不想在默认情况下打印或睡眠,但只是想保持循环呢?我试过这个:
case <-boom:
fmt.Println("BOOM!")
return
default: // Nothing here.
}
}
}
但它会阻止。
我在这里和那里看到一个关于goroutines调度的句子,但我不理解它们。所以我想我有两个问题:
1)为什么会阻止?
2)如何在没有阻止的情况下做任何事情?
答案 0 :(得分:5)
Your Original Example生成此</ p>
.
.
tick.
.
.
tick.
.
.
tick.
.
.
tick.
.
.
tick.
BOOM!
Wheraeas Your Second Example产生了这个
[process took too long]
区别在于你在default
案例中所做的。 default
案例随时可以运行,因此select
中的默认语句永远不会阻塞。第二个示例围绕循环运行,连续选择一个准备运行的分支(case或default)。您现在想知道为什么计时器永远不会触发。这是因为前程序没有先发制人。因此,因为下面的循环从不执行任何IO,所以时间节拍永远不会触发。
for {
select {
// whatever
default:
}
}
有很多方法可以解决这个问题。首先,你可以像在第一个例子中那样放入一些IO。或者您可以放入runtime.Gosched()。或者您可以允许go运行时使用多个runtime.GOMAXPROCS(2)的线程,所有这些线程都可以使用。
恕我直言的最佳方式是完全省略默认语句like this。没有默认语句的select将阻塞,直到其中一个case语句准备就绪。如果你想做一些后台处理(你在默认语句中做),那么就开始一个goroutine - 这就是去的方式!
事实上,我在select语句中看到了很多默认问题,我很想说它们从不使用它们。