为了账号安全,请及时绑定邮箱和手机立即绑定

当没有通道可供读取时,该怎么办?

当没有通道可供读取时,该怎么办?

Go
一只甜甜圈 2021-05-15 14:17:58
让我们以GoTour为例,它说明了我仅在有事件时才处理SDL事件的问题。package mainimport ("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.    }}}但它阻止了。我在这里和那里看到过有关goroutine调度的句子,但我不理解。所以我想我有两个问题:1)为什么会阻塞?2)如何使它不阻塞不执行任何操作?
查看完整描述

1 回答

?
桃花长相依

TA贡献1860条经验 获得超8个赞

你的原始例子产生了这个


    .

    .

tick.

    .

    .

tick.

    .

    .

tick.

    .

    .

tick.

    .

    .

tick.

BOOM!

Wheraeas您的第二个示例产生了这个


[process took too long]

区别在于您在该default案例中所做的事情。default案例总是可以运行,因此select带有默认语句的案例永远不会阻塞。第二个示例绕循环运行,不断选择准备运行的分支之一(大小写或默认)。您现在想知道为什么计时器永远不会触发。那是因为go例程没有被抢先调度。因此,由于下面的循环永远不会执行任何IO,因此时间间隔永远不会触发。


for {

    select {

        // whatever

        default:

    }

}

有许多解决此问题的方法。首先,您可以像在第一个示例中那样放置一些IO。或者你可以放入一个runtime.Gosched()。或者你可以允许 go 运行时使用多个线程与runtime.GOMAXPROCS(2)所有这些都可以工作。


恕我直言,最好的方法是完全不使用默认语句,就像这样。没有默认语句的选择将阻塞,直到其中一个case语句准备就绪。如果要进行一些后台处理(您在默认语句中所做的处理),请启动goroutine-这是可行的方法!


实际上,我在select语句中已经看到很多默认问题,所以我很想说永远不要使用它们。


查看完整回答
反对 回复 2021-05-31
  • 1 回答
  • 0 关注
  • 150 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信