我在使用time.Tick
时遇到了困难。我希望这段代码打印“hi”10次,然后在1秒后退出,但它会挂起:
ticker := time.NewTicker(100 * time.Millisecond)
time.AfterFunc(time.Second, func () {
ticker.Stop()
})
for _ = range ticker.C {
go fmt.Println("hi")
}
https://play.golang.org/p/1p6-ViSvma
查看source,我看到在调用Stop()
时通道未关闭。在这种情况下,迭代股票频道的惯用方法是什么?
答案 0 :(得分:2)
你是对的,自动收报机的频道没有在停止时关闭,这是documentation中所述:
停止关闭自动收报机。停止后,不再发送滴答声。 Stop不会关闭通道,以防止错误地从通道读取。
我相信自动收报机更多的是关于火灾和忘记,即使你想要阻止它,你甚至可以让例行程序永远停止(取决于你当然的应用)。
如果你真的需要一个有限的自动收报机,你可以做一些技巧并提供一个单独的渠道(根据ThunderCat的答案),但我要做的是提供我自己的自动收报机实现。这应该是相对容易的,并且会让你灵活地处理它的行为,比如在通道上传递什么或者决定如何处理丢失的刻度(即当读者落后时)。
我的例子:
func finiteTicker(n int, d time.Duration) <-chan time.Time {
ch := make(chan time.Time, 1)
go func() {
for i := 0; i < n; i++ {
time.Sleep(d)
ch <- time.Now()
}
close(ch)
}()
return ch
}
func main() {
for range finiteTicker(10, 100*time.Millisecond) {
fmt.Println("hi")
}
}
答案 1 :(得分:0)
我也在IRC上问过,从@Tv`获得了一些有用的见解。
尽管timer.Ticker
看起来应该是go pipeline的一部分,但它实际上并不适用于管道习语:
以下是管道建设的指南:
- 阶段在完成所有发送操作后关闭其出站通道。
- 阶段保持从入站通道接收值,直到这些通道关闭或发件人被解除阻止。
管道通过确保为发送的所有值提供足够的缓冲区或通过在接收方放弃信道时显式发送信号发送器来解锁发送方。
这种不一致的原因似乎主要是为了支持以下习语:
for {
select {
case <-ticker.C:
// do something
case <-done:
return
}
}
我不知道为什么会这样,为什么没有使用流水线习语:
for {
select {
case _, ok := <-ticker.C:
if ok {
// do something
} else {
return
}
}
}
(或更干净)
for _ = range ticker.C {
// do something
}
但这就是方法:(