select的奇怪行为(不允许其他goroutines运行)

时间:2012-10-14 17:48:08

标签: go

我正在尝试使用https://github.com/klkblake/Go-SDL编写SDL应用程序 我创建了计时器来调用draw函数:

render_timer := time.NewTicker(time.Second / 60)

事件循环中的某处:

for running == true {
    [...]
    [process sdl events]
    [...]
    select {
    case <-render_timer.C:
        call_my_draw_function()
    default:
        some_default_actions()
    }
    [...]
}

如果我在编译此代码后运行程序,则屏幕上不会显示任何内容。但如果我只是放置:

fmt.Println("default")
选择默认分支中的

- 代码开始按我的意愿工作(在窗口中绘制内容);如果我删除println它再次不绘制任何东西 我究竟做错了什么?为什么会出现选择的行为?


嗯...最简单的测试用例是:

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
    }
}

2 个答案:

答案 0 :(得分:7)

至于你的例子,你的循环是一个繁忙的循环,经常遇到default:的情况。 go中的调度程序是合作的,并且由于您处于繁忙的循环中,运行Ticker的go例程将永远不会被安排运行,因此从不在通道上发送任何内容。即使你的default:大小写不是空的,但是进行纯计算 - 从不进行任何调用schudler的调用,情况也是如此。

然而,当你做某事时,以某种形式调用go调度程序,例如执行I / O时,调度程序将为Ticker提供运行的机会。

您可以导入运行时包并执行

default:
    runtime.Gosched()

使调度程序运行,这不会使Ticker go例程挨饿。

我不确定这会导致运行SDL时遇到的问题,因为这很可能涉及I / O或触发调度程序的其他内容

答案 1 :(得分:6)

请参阅golang: goroute with select doesn't stop unless I added a fmt.Print()

长话短说,由于默认情况,并且因为您的GOMAXPROCS可能设置为1(默认值),所以它从不安排goroutine来完成其工作。有很多选项可以解决这个问题,具体取决于您的需求(默认情况下的睡眠,select中的time.After()通道而不是默认值,调用runtime.Gosched()等。)

添加fmt.Print使它工作,因为 - 我的猜测,不确定 - 它涉及io和内部导致Gosched()(在相关问题中,或者像Phlip的回答,我只是阅读它。)