没有准备好读取频道时怎么办?

时间:2013-01-31 19:13:35

标签: go channel

让我们从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)如何在没有阻止的情况下做任何事情?

1 个答案:

答案 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语句中看到了很多默认问题,我很想说它们从不使用它们。