Strange behaviour of select (does not allow other goroutines to run)
我正在尝试使用 https://github.com/klkblake/Go-SDL 在 Go 中编写 SDL 应用程序。
我创建了计时器来调用它的绘图函数:
1 | render_timer := time.NewTicker(time.Second / 60) |
事件循环中的某处:
1 2 3 4 5 6 7 8 9 10 11 12 | for running == true { [...] [process sdl events] [...] select { case <-render_timer.C: call_my_draw_function() default: some_default_actions() } [...] } |
如果我在编译此代码后运行程序,屏幕上不会绘制任何内容。但如果我只放置:
1 | fmt.Println("default") |
在 select 的默认分支中——代码开始按我的意愿工作(在窗口中绘制一些东西);如果我再次删除 println 它就不要画任何东西。
我究竟做错了什么?为什么会有这样的 select 行为?
嗯...最简单的测试用例是:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | package main import ( "fmt" "time" ) func main() { rt := time.NewTicker(time.Second / 60) for { select { case <-rt.C: fmt.Println("time") default: } time.Sleep(1) // without this line 'case <-rt.C' is never executed } } |
至于你的例子,你的循环是一个繁忙的循环,不断地碰到
然而,当你做一些以某种形式调用 go 调度器的事情时,例如做 I/O ,调度器会给 Ticker 一个运行的机会。
您可以导入运行时包并执行
1 2 | default: runtime.Gosched() |
让调度程序运行,这不会饿死 Ticker goroutine。
我不确定这会如何导致您在运行 SDL 时遇到问题,因为这很可能涉及 I/O 或其他触发调度程序的事情
请参阅 golang:除非我添加了 fmt.Print()
,否则带有 select 的 goroute 不会停止
长话短说,由于默认情况,并且因为您的 GOMAXPROCS 可能设置为 1(默认值),它从不安排 goroutine 完成其工作。有很多选项可以解决这个问题,具体取决于您的需要(默认睡眠、选择而不是默认的 time.After() 通道、对 runtime.Gosched() 的调用等)。
添加 fmt.Print 使其工作,因为 - 我的猜测,不确定 - 它涉及 io 并在内部导致 Gosched() (或者类似 Phlip 在相关问题中的答案,我刚刚阅读了它)。