英文:
Strange behaviour of select (does not allow other goroutines to run)
问题
我正在尝试使用<https://github.com/klkblake/Go-SDL>在Go语言中编写SDL应用程序。我创建了一个定时器来调用绘制函数:
render_timer := time.NewTicker(time.Second / 60)
在事件循环的某个地方:
for running == true {
[...]
[处理sdl事件]
[...]
select {
case <-render_timer.C:
调用我的绘制函数()
default:
一些默认操作()
}
[...]
}
如果我在编译这段代码后运行程序,屏幕上什么都不会绘制。但是如果我只是在_select_的_default_分支中放置:
fmt.Println("default")
代码就会按照我想要的方式工作(在窗口中绘制一些东西);如果我删除println,它又不会绘制任何东西。
我做错了什么?为什么_select_会有这样的行为?
嗯...最简单的测试用例是:
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) // 没有这一行,'case <-rt.C'永远不会执行
}
}
英文:
I'm trying to write SDL app in go using <https://github.com/klkblake/Go-SDL>.
I created timer to call draw function on it:
render_timer := time.NewTicker(time.Second / 60)
Somewhere in event loop:
for running == true {
[...]
[process sdl events]
[...]
select {
case <-render_timer.C:
call_my_draw_function()
default:
some_default_actions()
}
[...]
}
If I run program after compiling this code nothing is drawn on screen. But if I place just:
fmt.Println("default")
in the default branch of select -- the code begin to work as I want it to(draws something in window); and if i remove println it again don't draw anything.
What am I doing wrong? Why is there such behaviour of select ?
Hm... Simplest testcase is:
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
}
}
答案1
得分: 8
关于你的例子,你的循环是一个忙碌的循环,不断地命中default:
情况。Go中的调度器是协作式的,由于你在一个忙碌的循环中,运行Ticker的go例程将永远不会被调度运行,因此永远不会在通道上发送任何内容。即使你的default:
情况不为空,但是进行纯计算 - 从不调用调度器的调用,情况仍然如此。
然而,当你做一些其他以某种形式调用go调度器的操作时,例如进行I/O操作,调度器将给Ticker一个运行的机会。
你可以导入runtime包并执行以下操作:
default:
runtime.Gosched()
以使调度器运行,这样将不会使Ticker的go例程饥饿。
我不确定这会导致你在运行SDL时遇到的问题,因为那很可能涉及I/O或其他触发调度器的操作。
英文:
As for your example, your loop is a busy loop, constantly hitting the default:
case. The scheduler in go is co-operative, and since you're in a busy loop, the go routine running the Ticker will never be scheduled to run, and thus never send anything on the channel. This will be the case even if your default:
case is not empty, but do pure computations - that never make any calls that invoke the schudler.
However, when you do something else that in some form invokes the go scheduler, e.g. doing I/O , the scheduler will give the Ticker a chance to run.
You could import the runtime package and do
default:
runtime.Gosched()
to make the scheduler run, which will not starve the Ticker go routine.
I'm unsure how this leads to the problems you get when running SDL, as that would most likely involve I/O or something else that triggers the scheduler
答案2
得分: 6
长话短说,由于默认情况下存在default case,并且由于您的GOMAXPROCS可能设置为1(默认值),它从不安排goroutine执行其工作。有许多选项可以解决此问题,具体取决于您的需求(在default中添加一个sleep,将time.After()通道放入select而不是default,调用runtime.Gosched()等)。
添加fmt.Print使其工作,因为-我猜测,不确定-它涉及io并在内部导致Gosched()(或者类似于Phlip在相关问题中的答案,我刚刚阅读过)。
英文:
Long story short, because of the default case, and because your GOMAXPROCS is probably set to 1 (the default value), it never schedules the goroutine to do its work. There are many options to fix this, depending on your needs (a sleep in default, a time.After() channel in the select instead of the default, a call to runtime.Gosched(), etc.).
Adding the fmt.Print makes it work because - my guess, not sure - it involves io and internally causes a Gosched() (that or something like Phlip's answer in the related question, I just read it).
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论